Go: HTTP и сети

19 вопросов

1 HTTP/1.1, HTTP/2, HTTP/3. Поддержка в Go.

HTTP/1.1 - по умолчанию в net/http (одно соединение - один запрос за раз без pipelining). HTTP/2 включен автоматически при TLS (h2) или через x/net/http2; мультиплексирование, один connection на много запросов. HTTP/3 (QUIC) - экспериментально в Go (quic-go). В Go сервер и клиент поддерживают HTTP/2 при наличии TLS или при явной регистрации.

import _ "golang.org/x/net/http2"
srv := &http.Server{...}
http2.ConfigureServer(srv, nil)
Открыть отдельно →
2 HTTPS и TLS в Go. Настройка сервера и клиента.

Сервер: передать TLSConfig и сертификаты в http.Server, слушать через ListenAndServeTLS(":443", "cert.pem", "key.pem", handler). Клиент: tls.Config с RootCAs или InsecureSkipVerify (только для тестов), http.Transport с TLSClientConfig. Для production загрузка сертификатов из файлов или системы; минимум TLS 1.2.

srv := &http.Server{
    Addr:      ":443",
    Handler:   mux,
    TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12},
}
srv.ListenAndServeTLS("cert.pem", "key.pem")
Открыть отдельно →
3 TCP и UDP в Go. Основные API.

TCP: net.Listen("tcp", ":8080"), Accept(), Read/Write или bufio. UDP: net.ListenPacket("udp", ":8080"), ReadFrom/WriteTo. Для HTTP используется TCP под капотом. TCP надежный, с установкой соединения; UDP без установки, без гарантий доставки. В Go типично net.Conn для TCP, net.PacketConn для UDP (DNS, игровые протоколы).

ln, _ := net.Listen("tcp", ":8080")
conn, _ := ln.Accept()
defer conn.Close()
conn.Write([]byte("data"))
Открыть отдельно →
4 DNS в Go. Резолвинг и подмена для тестов.

Резолвинг через net.LookupHost, net.ResolveTCPAddr и при установке соединения (по умолчанию используется системный резолвер). Подмена для тестов: кастомный Resolver с полем Dial или подмена net.DefaultResolver. Для контроля над DNS (например, fallback) используют пакет net с Resolver.PreferGo и кастомным Dial.

addrs, err := net.LookupHost("example.com")
r := &net.Resolver{PreferGo: true}
ctx := context.Background()
addrs, err := r.LookupHost(ctx, "example.com")
Открыть отдельно →
5 Методы HTTP и идемпотентность. Реализация в Go.

GET - получение, идемпотентный. POST - создание/действие, не идемпотентный. PUT - замена ресурса, идемпотентный. PATCH - частичное обновление. DELETE - удаление, идемпотентный. В Go в handler проверяют r.Method: switch r.Method { case http.MethodGet: ... case http.MethodPost: ... }. Для идемпотентности повторный PUT/DELETE с теми же данными дают тот же результат; при ретраях безопасны.

if r.Method != http.MethodPost {
    http.Error(w, "Method Not Allowed", 405)
    return
}
Открыть отдельно →
6 HTTP коды ответа. Когда какой использовать в Go.

2xx: 200 OK, 201 Created (и Location), 204 No Content. 3xx: 301/302 редирект. 4xx: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Entity. 5xx: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable. В Go: w.WriteHeader(code), затем w.Write(body). Для редиректа http.Redirect(w, r, url, code).

w.WriteHeader(http.StatusCreated)
w.Header().Set("Location", "/users/"+id)
json.NewEncoder(w).Encode(user)
Открыть отдельно →
7 Принципы REST и проектирование API в Go.

Ресурсы по URI, методы HTTP для действий, stateless, представления (JSON). В Go: маршрутизатор (mux) по пути и методу, handler возвращает JSON (Content-Type, сериализация). Вложенные ресурсы: /users/1/orders. Версионирование: префикс /v1/ или заголовок. Коды ошибок и тело ошибки в едином формате.

mux.HandleFunc("/users", handleUsers)
mux.HandleFunc("/users/", handleUserByID)
Открыть отдельно →
8 Идемпотентность запросов. Как обеспечить в Go?

GET, PUT, DELETE идемпотентны по спецификации. POST не идемпотентен; для повторных отправок используют идемпотентные ключи: клиент шлет Idempotency-Key, сервер кеширует результат по ключу и при повторном запросе возвращает тот же ответ. В Go: middleware извлекает ключ, проверяет кеш (Redis/БД), при совпадении возвращает сохраненный ответ, иначе выполняет handler и сохраняет результат.

key := r.Header.Get("Idempotency-Key")
if key != "" {
    if cached, ok := cache.Get(key); ok { writeResponse(w, cached); return }
}
resp := doWork()
cache.Set(key, resp)
writeResponse(w, resp)
Открыть отдельно →
9 CORS в Go. Настройка заголовков.

CORS - разрешение cross-origin запросов. Браузер шлет preflight (OPTIONS); сервер отвечает Access-Control-Allow-Origin, Allow-Methods, Allow-Headers. Для простого случая: разрешить один origin или * (для публичного API). В Go middleware проверяет Origin, выставляет заголовки, для OPTIONS возвращает 204. Credentials - Allow-Credentials: true и конкретный Origin (не *).

w.Header().Set("Access-Control-Allow-Origin", "https://app.example.com")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
if r.Method == http.MethodOptions { w.WriteHeader(204); return }
Открыть отдельно →
10 JWT и OAuth2 в Go. Библиотеки и паттерны.

JWT: подпись и проверка claims (например, golang-jwt/jwt). После логина выдают токен, клиент шлет в Authorization: Bearer . Проверка в middleware: извлечь токен, парсить, проверить подпись и exp. OAuth2: oauth2 пакет (client); для провайдера - выдача кода, обмен на токен, защита ресурсов по токену. В Go часто JWT для API и OAuth2 для "войти через Google".

token, err := jwt.ParseWithClaims(bearer, &claims, keyFunc)
if err != nil || !token.Valid { http.Error(w, "Unauthorized", 401); return }
r = r.WithContext(context.WithValue(r.Context(), "user", claims.Subject))
Открыть отдельно →
11 WebSocket в Go. Библиотеки и обработка.

Протокол по HTTP Upgrade. В стандартной библиотеке нет готового WebSocket; используют gorilla/websocket или nhooyr.io/websocket. После Upgrade получают Conn; чтение и запись в цикле (ReadMessage, WriteMessage). Важно: обрабатывать закрытие и таймауты, ограничивать размер сообщения. Для масштабирования - sticky session на балансировщике или pub/sub между инстансами.

upgrader := websocket.Upgrader{}
conn, _ := upgrader.Upgrade(w, r, nil)
defer conn.Close()
for { _, msg, err := conn.ReadMessage(); ... }
Открыть отдельно →
12 SSE (Server-Sent Events) в Go.

Односторонний поток с сервера: соединение держится открытым, данные в формате text/event-stream (data:, id:, event:). В Go: w.Header().Set("Content-Type", "text/event-stream"), w.Flush() после каждой отправки, писать события в цикле. Клиент - EventSource. Удобно для уведомлений и лент. Таймауты и keep-alive (комментарии) для сохранения соединения.

w.Header().Set("Content-Type", "text/event-stream")
flusher := w.(http.Flusher)
fmt.Fprintf(w, "data: %s\n\n", data)
flusher.Flush()
Открыть отдельно →
13 HTTP кеширование. Заголовки в Go.

Cache-Control (max-age, no-cache, no-store, private, public), ETag, Last-Modified. При повторном запросе клиент шлет If-None-Match (ETag) или If-Modified-Since; сервер отвечает 304 Not Modified при неизменности. В Go выставляют заголовки в ответе; для 304 сравнивают If-None-Match с текущим ETag и при совпадении пишут 304 без тела.

w.Header().Set("Cache-Control", "public, max-age=3600")
w.Header().Set("ETag", "\""+etag+"\"")
if r.Header.Get("If-None-Match") == "\""+etag+"\"" {
    w.WriteHeader(304)
    return
}
Открыть отдельно →
14 Keep-alive и пул соединений в Go HTTP-клиенте.

HTTP/1.1 keep-alive - одно TCP-соединение для нескольких запросов. В Go http.Client по умолчанию переиспользует соединения (Transport с пулом). Настройка: MaxIdleConns, MaxIdleConnsPerHost, IdleConnTimeout. Не закрывать тело ответа до полного прочтения - иначе соединение не вернется в пул. Для большого числа запросов увеличить MaxIdleConnsPerHost.

client := &http.Client{
    Transport: &http.Transport{
        MaxIdleConnsPerHost: 100,
        IdleConnTimeout:     90 * time.Second,
    },
    Timeout: 30 * time.Second,
}
Открыть отдельно →
15 CDN и Go-приложение. Что отдавать через CDN.

Через CDN отдают статику (изображения, CSS, JS) и при необходимости кешируемые API-ответы. Go-приложение - origin; отдает заголовки Cache-Control и ETag. CDN кеширует по этим заголовкам; инвалидация по URL или ключу. Для статики в Go часто используют embed или отдельный сервис; для API - короткий max-age или no-cache с ETag.

Открыть отдельно →
16 Rate limiting в Go. Реализация на уровне handler.

Ограничение частоты запросов по ключу (IP, user id, API key). Алгоритмы: фиксированное окно, скользящее окно, token bucket. В Go: middleware с хранилищем (map + mutex или Redis для распределенного). При превышении возвращают 429 Too Many Requests и Retry-After. Библиотеки: golang.org/x/time/rate (token bucket), ulule/limiter.

limiter := rate.NewLimiter(10, 20) // 10/sec, burst 20
if !limiter.Allow() {
    http.Error(w, "Too Many Requests", 429)
    return
}
Открыть отдельно →
17 Пакет net/http. Основные типы.

http.Server - сервер (Addr, Handler, ReadTimeout, WriteTimeout). http.Handler - интерфейс ServeHTTP(w, r). http.HandlerFunc - адаптер func(w, r). http.Request - метод, URL, заголовки, тело. http.ResponseWriter - WriteHeader, Write, Header(). ListenAndServe запускает сервер. Клиент: http.Get/Post или http.Client с кастомным Transport и Timeout.

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "OK")
}
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
Открыть отдельно →
18 Интерфейс http.Handler и цепочки middleware в Go.

Handler обрабатывает запрос: ServeHTTP(w, r). Middleware - функция func(http.Handler) http.Handler: оборачивает следующий handler (логирование, auth, recovery). Цепочка: h = middleware1(middleware2(finalHandler)). В Go типично передавать next в замыкание и вызывать next.ServeHTTP(w, r) после проверок. Альтернатива - chi, echo с встроенным middleware.

func logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.URL.Path)
        next.ServeHTTP(w, r)
    })
}
mux := logging(http.DefaultServeMux)
Открыть отдельно →
19 Reverse proxy в Go. net/http/httputil.

httputil.ReverseProxy перенаправляет запросы на другой сервер. NewSingleHostReverseProxy(url) создает прокси к одному бэкенду. В Director меняют Host, Scheme, путь. Используют для балансировки, скрытия бэкендов, добавления заголовков. Для нескольких бэкендов свой Director с выбором по правилу (round-robin, sticky).

target, _ := url.Parse("http://backend:8080")
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Director = func(r *http.Request) {
    r.URL.Scheme = target.Scheme
    r.URL.Host = target.Host
    r.Host = target.Host
}
http.ListenAndServe(":80", proxy)
Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.