linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Сеть: L4 и выше/grpc-basics

kb/network-l4 ── Сеть: L4 и выше ── intermediate

gRPC - HTTP/2 + Protobuf RPC framework

gRPC = HTTP/2 + Protocol Buffers + кодогенерация. Четыре типа RPC: unary (как REST), server-stream, client-stream, bidirectional. Сильная типизация, бинарный wire format, multi-language. grpcurl как curl для gRPC.

view as markdownaka: grpc, g-rpc, protobuf, grpc-go, grpc-python

Зачем gRPC

REST + JSON хорош для public API, но проблематичен для internal microservices:

  • Schema-less: контракт неявный, ломается на refactor
  • Текстовый JSON - дольше парсить, больше bytes
  • Нет streaming - real-time события через polling/SSE/WebSocket
  • Нет кодогенерации - руками писать клиента под каждый язык

gRPC (Google RPC, 2015, open source) решает:

  • Контракт через .proto - сильная типизация, schema-driven
  • Protobuf wire format - бинарный, компактный, быстрый
  • HTTP/2 transport - multiplexing, binary framing, [[http2-internals|streaming]]
  • Кодогенерация - один .proto → клиенты на 11 языках
  • Built-in deadlines, retries, load balancing

Применения:

  • Internal microservices (Google, Netflix, Square, Lyft)
  • Service mesh (Istio Pilot ↔ Envoy через gRPC)
  • Kubernetes (kubelet ↔ CRI-runtime через gRPC)
  • CockroachDB, etcd, dgraph - internal RPC

Не для public web - браузеры HTTP/2 поддерживают, но сырой gRPC требует библиотеку (нет встроенной поддержки в fetch()). gRPC-Web через прокси решает (Envoy translates).

.proto - язык контрактов

proto
syntax = "proto3";
package myservice;
option go_package = "github.com/me/myservice/pb";
service UserService {
    rpc GetUser(GetUserRequest) returns (User);
    rpc ListUsers(ListUsersRequest) returns (stream User);
    rpc CreateUsers(stream CreateUserRequest) returns (CreateUsersResponse);
    rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
message GetUserRequest {
    string user_id = 1;
}
message User {
    string id = 1;
    string email = 2;
    int64 created_at = 3;
    repeated string roles = 4;
}

Каждое поле имеет:

  • Тип (string/int32/int64/bool/bytes/message/enum/repeated/map)
  • Tag (1, 2, 3 - wire-формат использует tag, не имя)
  • Опции (optional, deprecated, json_name)

Backward compatibility: добавление поля с новым tag - safe; переиспользование старого tag - ломает старых клиентов.

Кодогенерация

Для каждого языка есть protoc plugin:

# Go
protoc --go_out=. --go-grpc_out=. user.proto
# Python
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. user.proto
# TypeScript
protoc --plugin=protoc-gen-ts_proto --ts_proto_out=. user.proto

Генерится:

  • Структуры данных для message'й (GetUserRequest, User, ...)
  • Stub для клиента (UserServiceClient)
  • Interface для сервера (UserServiceServer - implement)

Идиоматичный код, не reflection-based - быстрый.

Четыре типа RPC

1. Unary - как REST

proto
rpc GetUser(GetUserRequest) returns (User);

Клиент шлёт один request, получает один response. 99% gRPC-вызовов в microservices - именно unary.

go
resp, err := client.GetUser(ctx, &pb.GetUserRequest{UserId: "u123"})

2. Server streaming

proto
rpc ListUsers(ListUsersRequest) returns (stream User);

Клиент шлёт один request, сервер отправляет несколько ответов через тот же stream. Завершается когда сервер закрывает.

go
stream, _ := client.ListUsers(ctx, &pb.ListUsersRequest{})
for {
    user, err := stream.Recv()
    if err == io.EOF { break }
    handle(user)
}

Применение: pagination, exports, server-sent events.

3. Client streaming

proto
rpc CreateUsers(stream CreateUserRequest) returns (CreateUsersResponse);

Клиент шлёт несколько request'ов, сервер отвечает один раз когда client закрывает stream. Применение: bulk-upload, file streaming.

4. Bidirectional streaming

proto
rpc Chat(stream ChatMessage) returns (stream ChatMessage);

Обе стороны шлют независимо. Похоже на websocket, но с schema-typed messages. Применение: chat, real-time collab, Envoy xDS streaming config updates.

Wire format - HTTP/2 + protobuf

gRPC-вызов на проводе:

HEADERS
  :method = POST
  :scheme = https
  :path = /myservice.UserService/GetUser
  :authority = api.example.com
  content-type = application/grpc
  grpc-encoding = gzip
  grpc-accept-encoding = identity, gzip
  user-agent = grpc-go/1.50.0
DATA
  [00] [00 00 00 24] [<protobuf-encoded GetUserRequest>]
  │     │             │
  │     │             └── 36 байт payload
  │     └── length (big-endian uint32)
  └── compressed-flag (0=no, 1=yes)
HEADERS (trailers)
  grpc-status = 0
  grpc-message =
  • Path = /<package>.<Service>/<Method>
  • content-type = application/grpc (или application/grpc+proto)
  • DATA frame содержит gRPC message frame: 1 байт compressed-flag
    • 4 байта длины + payload (protobuf bytes)
  • trailers (HEADERS после DATA) несут grpc-status и grpc-message

Status codes (grpc-status) отдельные от HTTP:

  • 0 = OK
  • 1 = CANCELLED
  • 2 = UNKNOWN
  • 3 = INVALID_ARGUMENT
  • 4 = DEADLINE_EXCEEDED
  • 5 = NOT_FOUND
  • 13 = INTERNAL
  • 14 = UNAVAILABLE (retry-able)

HTTP/2 :status всегда 200 (даже если gRPC-call провалился).

TLS - mutual auth и cert

gRPC обычно с TLS (grpc.WithTransportCredentials). Для микросервисов

  • mTLS через [[tls-certificates|клиент-сертификаты]]:
go
creds := credentials.NewTLS(&tls.Config{
    Certificates: []tls.Certificate{clientCert},
    RootCAs:      caPool,
})
conn, _ := grpc.Dial("api:443", grpc.WithTransportCredentials(creds))

В service mesh (Istio, Linkerd) - mTLS делается sidecar'ом, app шлёт plaintext в localhost:5500.

Deadlines, метаданные, retry

Deadlines

Каждый client RPC имеет deadline (абсолютное время cancellation). Через context:

go
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
client.GetUser(ctx, ...)

Deadline передаётся серверу через header grpc-timeout. Сервер знает - если deadline прошёл, сразу вернёт DEADLINE_EXCEEDED, не тратя ресурсы. Каскадно передаётся дальше в downstream calls.

Metadata - кастомные headers

go
md := metadata.Pairs("auth-token", "secret123", "x-request-id", "abc")
ctx = metadata.NewOutgoingContext(ctx, md)

На сервере:

go
md, _ := metadata.FromIncomingContext(ctx)
token := md.Get("auth-token")

Retry config

Через service config (JSON в connection options):

json
{
  "methodConfig": [{
    "name": [{"service": "myservice.UserService"}],
    "retryPolicy": {
      "maxAttempts": 3,
      "initialBackoff": "0.1s",
      "maxBackoff": "1s",
      "backoffMultiplier": 2,
      "retryableStatusCodes": ["UNAVAILABLE"]
    }
  }]
}

Load balancing

gRPC поддерживает client-side LB через resolver + LB policy:

  • Resolver получает список backend'ов (DNS, etcd, xDS)
  • LB policy выбирает backend для каждого RPC (round_robin, pick_first)

В Kubernetes стандартный паттерн - headless Service + DNS resolver. ClusterIP-Service не работает: gRPC использует один long-lived HTTP/2 connection - kube-proxy балансирует только на TCP-уровне → все запросы идут на один pod.

Альтернатива - xDS-based balancing (через Envoy/gRPC xDS API), proxyless service mesh.

gRPC vs REST/JSON - таблица

СвойствоREST/JSONgRPC
КонтрактOpenAPI (опц).proto (обяз)
Wire formattext JSONbinary protobuf
Размер на проводебазовый30-60% меньше
Скорость parseмедленнее (~5x)быстрее
Streamingчерез SSE/WebSocketпервоклассно
Browser supportnativeчерез gRPC-Web proxy
Schema evolutionрукамиконтролируется через tag
Toolingcurl, Postmangrpcurl, BloomRPC
Use casepublic APIinternal microservices

Часто рядом - REST для внешнего API, gRPC для внутреннего. Gateway транслирует (grpc-gateway).

gRPC и Kubernetes Ingress

Дефолт Ingress NGINX поддерживает gRPC через annotation:

yaml
metadata:
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: GRPC

Envoy/Istio - native поддержка. Гибче в:

  • Путях RPC (/myservice.UserService/*)
  • Per-method route и rate-limit
  • Retries по конкретным status codes

См. [[kubernetes-services-and-ingress|k8s services and ingress]] для основ.

Когда что-то пошло не так

  • UNAVAILABLE: connection refused - server не слушает или network. Проверь kubectl exec ... grpcurl host:port list.
  • Все запросы на один pod - ClusterIP-сервис ломает gRPC LB. Перейти на headless service + DNS resolver.
  • UNIMPLEMENTED - метод не зарегистрирован на сервере. Проверь что использует тот же .proto.
  • Deadline immediately exceeded - context.WithTimeout уже прошёл к моменту вызова. Проверь chain контекстов.
  • INTERNAL: stream terminated by RST_STREAM - HTTP/2-уровень ошибка, обычно proxy timeout (Envoy idle timeout 1h, NGINX 60s). tcpdump + tshark -Y http2.
  • max-message size exceeded - default gRPC msg limit 4 MB. grpc.MaxRecvMsgSize(64<<20) поднять.
  • HPACK ошибки - см. http2-internals, часто bug в server library.

Полезные tools

  • grpcurl - curl для gRPC, использует server-reflection или .proto
  • buf - современный Protobuf builder/linter (заменяет protoc)
  • BloomRPC, Postman - GUI клиенты для interactive testing
  • ghz - load testing (как hey/wrk для gRPC)
  • server-reflection - сервер описывает свои RPC, grpcurl без .proto

§ команды

bash
grpcurl -plaintext localhost:50051 list

Список services через server-reflection (если включён в server)

bash
grpcurl -plaintext -d '{"user_id":"u1"}' localhost:50051 myservice.UserService/GetUser

Сделать unary RPC - JSON автоматически конвертится в protobuf

bash
grpcurl -import-path . -proto user.proto -d '{}' localhost:50051 myservice.UserService/ListUsers

Без reflection - указать .proto явно

bash
protoc --go_out=. --go-grpc_out=. -I=proto user.proto

Сгенерить Go-код из .proto (нужны protoc-gen-go и protoc-gen-go-grpc)

bash
buf generate

Современная альтернатива protoc - buf использует buf.yaml + buf.gen.yaml

bash
ghz --proto user.proto --call myservice.UserService.GetUser -d '{}' -c 100 -n 10000 host:50051

Load test 100 concurrent, 10K total RPC

bash
tshark -Y 'http2 && http2.headers.path contains grpc' -i eth0

Перехват gRPC-трафика в Wireshark CLI

bash
openssl s_client -alpn h2 -connect grpc.example.com:443 < /dev/null 2>&1 | grep -i alpn

Проверить, что endpoint advertise'ит HTTP/2 через ALPN (нужно для gRPC)

§ см. также

  • http2-internalsHTTP/2 internals - binary framing, HPACK, stream multiplexingHTTP/2 - бинарный мультиплексинг поверх одного TCP-соединения. HPACK сжимает headers через индексированный словарь. Streams независимы. Server push deprecated. На loss-friendly link HoL-blocking - проблема, которую решил QUIC.
  • http-protocolHTTP/1.1, HTTP/2, HTTP/3HTTP/1.1 - текстовый протокол с keep-alive. HTTP/2 - бинарный с мультиплексированием в одном TCP-соединении. HTTP/3 = HTTP/2-семантика поверх QUIC/UDP без TCP-head-of-line blocking.
  • tls-certificatesTLS-сертификаты - X.509, цепочка доверия, Let's EncryptTLS cert - X.509 объект с public key + identity (CN/SAN) + подписью CA. Цепочка: leaf → intermediate → root (доверенный OS). Let's Encrypt = бесплатный CA через ACME (HTTP-01/DNS-01 challenge). В k8s - cert-manager.
  • kubernetes-services-and-ingressKubernetes Service и Ingress - сетевая публикация подовService - стабильный VIP перед группой подов (label selector). Типы: ClusterIP (внутри), NodePort (порт на каждой ноде), LoadBalancer (внешний LB облака), ExternalName (CNAME). Ingress - L7 reverse-proxy (nginx/traefik) для HTTP-роутинга.
  • websocketWebSocket - bidirectional поверх HTTPWebSocket - двусторонний канал поверх одного TCP. Апгрейд из HTTP/1.1 через Upgrade header, потом обмен бинарными frame'ами. Используется для real-time UI - чаты, дашборды, live-обновления.
  • opentelemetryOpenTelemetry: signals, OTLP, Collector pipelineOpenTelemetry - CNCF-стандарт для metrics+traces+logs в одном SDK. OTLP протокол (gRPC или HTTP). Collector принимает, фильтрует, роутит в Prom/Tempo/Loki/Jaeger. Auto-instrumentation без code change.
  • tracing-basicsDistributed tracing: span, context propagation, samplingTracing - граф spans (parent-child) одного логического запроса через сервисы. Context передаётся HTTP-header traceparent (W3C). Sampling: head (на edge, дёшево) или tail (в Collector, точнее). Backend: Jaeger, Tempo, Zipkin.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки