gRPC-GoMiddleSystem design

Что такое google.golang.org/grpc/keepalive и почему это важно для продакшен-развёртываний?

Пакет keepalive управляет HTTP/2 PING-фреймами для обнаружения мёртвых соединений. Без него соединения тихо умирают за NAT/firewall. Настройте ServerParameters, ClientParameters и EnforcementPolicy — значения должны быть согласованы.

google.golang.org/grpc/keepalive и его роль в production

Пакет google.golang.org/grpc/keepalive управляет механизмом HTTP/2 PING-фреймов для поддержания активности соединений и обнаружения «мёртвых» подключений. Без корректной настройки keepalive gRPC-соединения тихо умирают за NAT, firewall или cloud load balancer.

Параметры серверной стороны

import (
	"time"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

serverParams := keepalive.ServerParameters{
	// Пинговать клиента если нет активности N времени
	Time:              2 * time.Hour,
	// Ждать ответа на PING не более N секунд
	Timeout:           20 * time.Second,
	// Закрыть idle-соединения через N времени
	MaxConnectionIdle: 15 * time.Minute,
	// Принудительно закрывать все соединения через N
	MaxConnectionAge:  30 * time.Minute,
	// Grace-период после MaxConnectionAge
	MaxConnectionAgeGrace: 5 * time.Second,
}

serverEnforcement := keepalive.EnforcementPolicy{
	// Минимальный интервал PING от клиента
	MinTime:             5 * time.Minute,
	// Разрешить PING без активных стримов
	PermitWithoutStream: true,
}

srv := grpc.NewServer(
	grpc.KeepaliveParams(serverParams),
	grpc.KeepaliveEnforcementPolicy(serverEnforcement),
)

Параметры клиентской стороны

clientParams := keepalive.ClientParameters{
	// Отправлять PING если нет активности N времени
	Time:                10 * time.Second,
	// Ждать ответа на PING не более N секунд
	Timeout:             5 * time.Second,
	// Пинговать даже без активных RPC
	PermitWithoutStream: true,
}

conn, err := grpc.NewClient(
	"localhost:50051",
	grpc.WithTransportCredentials(insecure.NewCredentials()),
	grpc.WithKeepaliveParams(clientParams),
)

Почему keepalive критичен для production

  • NAT timeout: AWS NAT Gateway закрывает idle TCP-соединения через 350 секунд. Без PING клиент держит «мёртвое» соединение и получает ошибки на следующем запросе.
  • Firewall: корпоративные и облачные firewall закрывают неактивные соединения через 30–600 секунд.
  • Load balancer: AWS ALB/NLB, GCP Load Balancer имеют таймауты idle-соединений (по умолчанию 60–600 секунд).
  • Обнаружение упавшего сервера: без PING клиент не узнает о падении сервера до следующего запроса.

EnforcementPolicy — защита от PING-флуда

Если клиент шлёт PING слишком часто (меньше MinTime), сервер отправляет GOAWAY с кодом ENHANCE_YOUR_CALM. Это антиспам-механизм:

enforcement := keepalive.EnforcementPolicy{
	MinTime:             5 * time.Minute, // клиент не должен пинговать чаще
	PermitWithoutStream: false,           // запретить PING без активных стримов
}

Диагностика через channelz

import "google.golang.org/grpc/channelz/service"

// channelz показывает статистику keepalive пингов
service.RegisterChannelzServiceToServer(srv)
// Запросите /channelz/v1/channels через grpcurl

Подводные камни

  • Если клиентский Time меньше серверного MinTime, сервер отправит GOAWAY ENHANCE_YOUR_CALM и разорвёт соединение — несогласованные настройки ломают систему.
  • PermitWithoutStream: true на клиенте при PermitWithoutStream: false на сервере приведёт к принудительному закрытию соединения.
  • Слишком агрессивный Time (например, 1 секунда) создаёт лишний трафик и нагружает сервер — минимум 10 секунд для production.
  • MaxConnectionAge без MaxConnectionAgeGrace обрывает активные стримы без grace-периода.
  • Keepalive PING работает на уровне соединения, а не отдельных стримов — один PING обслуживает все RPC на соединении.
  • В Kubernetes при terminationGracePeriodSeconds меньше MaxConnectionAge клиенты не успевают переподключиться при деплое.
  • Nginx не проксирует HTTP/2 keepalive прозрачно — при использовании nginx как gRPC proxy проверьте grpc_read_timeout и grpc_send_timeout.

Common mistakes

  • Давать ответ про keepalive в gRPC-Go только на уровне определения, не показывая поведение в реальном приложении.
  • Игнорировать границы ответственности вокруг темы «keepalive в gRPC-Go»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
  • Не связывать keepalive в gRPC-Go с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.

What the interviewer is testing

  • Точно объясняет, что именно делает keepalive в gRPC-Go и где это используется в Go-коде.
  • Связывает keepalive в gRPC-Go с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
  • Не изобретает API и опирается на реальные контракты официальной документации.

Sources

Related topics