← Назад к вопросам

Что происходит с машиной при сетевом запросе?

2.0 Middle🔥 131 комментариев
#Операционные системы и Linux#Сетевые протоколы и API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Полный путь сетевого запроса в Go

Когда в Go приложении выполняется сетевой запрос (например, через http.Client), происходит сложная последовательность событий, охватывающая несколько уровней абстракции — от высокоуровневого API до системных вызовов в ядре операционной системы.

1. Высокоуровневый API и создание запроса

На уровне приложения мы используем стандартный пакет net/http. Запрос начинается с создания http.Request и выполнения через http.Client.

// Пример высокоуровневого запроса
req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
if err != nil {
    log.Fatal(err)
}

client := &http.Client{}
resp, err := client.Do(req)

http.Client.Do() начинает сложный процесс:

  • Парсинг URL, определение схемы (HTTP/HTTPS)
  • Добавление заголовков, обработка тела запроса
  • Проверка настроек клиента: Timeout, Transport, прокси

2. Транспортный уровень (http.Transport)

Ключевой компонент — http.Transport, который управляет пулом соединений и реализует HTTP-протокол.

// Transport управляет пулом TCP соединений и повторными попытками
transport := &http.Transport{
    MaxIdleConns:        100,
    IdleConnTimeout:     90 * time.Second,
    TLSHandshakeTimeout: 10 * time.Second,
}

Transport выполняет:

  • Поиск существующего idle соединения из пула для данного хоста
  • Если соединения нет — создание нового TCP соединения
  • Для HTTPS — установка TLS-сессии через crypto/tls
  • Управление keep-alive соединениями для повторного использования
  • Следование политикам IdleConnTimeout и MaxIdleConnsPerHost

3. Сетевой уровень и системные вызовы

Когда требуется новое TCP соединение, Go вызывает низкоуровневые системные функции через пакет net.

// Пример низкоуровневого TCP соединения
conn, err := net.DialTimeout("tcp", "api.example.com:443", 5*time.Second)

Происходит:

  1. Разрезолвинг DNS: Если адрес не IP — выполняется DNS запрос через системный резолвер или настроенный (например, через net.Resolver).
  2. TCP handshake: Системный вызов socket()connect() для установки TCP соединения через трехэтапное рукопожатие (SYN, SYN-ACK, ACK).
  3. Установка буферов: Go создает буферы чтения/записи для соединения, используя неблокирующий I/O с помощью goroutine.

4. TLS handshake для HTTPS

Для HTTPS соединений после TCP устанавливается TLS сессия через пакет crypto/tls.

// TLS конфигурация и рукопожатие
tlsConn := tls.Client(conn, &tls.Config{
    ServerName: "api.example.com",
})

TLS процесс включает:

  • Обмен ClientHello и ServerHello
  • Аутентификация сервера (проверка сертификата)
  • Согласование алгоритмов шифрования
  • Генерация ключей для симметричного шифрования
  • Установка защищенного TLS-туннеля

5. Передача HTTP данных

После установки соединения (TCP или TLS) передаются HTTP данные.

На этом этапе:

  • HTTP/1.1: Заголовки и тело отправляются как текстовый поток
  • HTTP/2: Используется binary framing, multiplexing (если поддерживается)
  • Запись в сокет: Данные копируются из буферов Go в системные буферы сокета через системный вызов write()
  • Ожидание ответа: Go scheduler управляет goroutine, которая ожидает данные с помощью epoll/kqueue (неблокирующее ожидание)

6. Обработка ответа и чтение данных

Когда сервер отвечает, происходит обратный процесс:

// Чтение ответа из соединения
resp, err := http.ReadResponse(bufio.NewReader(conn), req)

Этапы чтения:

  1. Считывание заголовков: Парсинг HTTP заголовков из сетевого потока
  2. Чтение тела: Потоковое чтение тела ответа с учетом Content-Length или chunked encoding
  3. Декодирование: При необходимости — декодирование (gzip, deflate)
  4. Возврат в пул: После полного чтения соединение может возвращаться в пул idle соединений для повторного использования

7. Критические аспекты производительности

Пул соединений — ключевая оптимизация, предотвращающая повторные TCP/TLS handshake. Однако пул требует управления:

  • Idle timeout для закрытия неиспользуемых соединений
  • Max conns per host для ограничения параллельных запросов
  • Cleanup goroutine для периодической очистки пула

Неблокирующий I/O благодаря goroutine позволяет тысячам одновременных запросов без thread-per-connection модели.

Заключение

Путь сетевого запроса в Go — это многоуровневый процесс от высокоуровневого HTTP API до системных вызовов ядра, эффективно управляемый через goroutine, неблокирующий I/O и пул соединений. Правильная конфигурация http.Transport критически важна для производительности сетевых приложений, особенно при высоких нагрузках и необходимости повторного использования TLS соединений.

Что происходит с машиной при сетевом запросе? | PrepBro