글 / gRPC 통신

gRPC Keepalive 설정

Keepalive는 PING으로 단절된 연결을 감지하지만, 서버의 허용 간격보다 짧은 주기로 전송하면 연결이 종료된다.

작성일 · 읽는 시간 3분 · Grpcio 1.83 기준

Keepalive의 필요성

장시간 데이터 전송이 없는 연결은 경로상의 프록시나 로드 밸런서가 유휴(idle) 연결로 판단하여 종료할 수 있다. 양측은 이를 감지하지 못한 채 열려 있는 호출에서 계속 대기한다. keepalive는 HTTP/2 PING을 주기적으로 전송하고, 지정한 시간 내에 응답이 없으면 연결을 닫는다.

클라이언트 옵션

클라이언트의 keepalive는 기본적으로 비활성화되어 있다. 채널 옵션으로 활성화한다.

pythonclient.py
import grpc

options = [
    # 60초마다 PING을 보낸다
    ("grpc.keepalive_time_ms", 60000),
    # 20초 안에 응답이 없으면 연결을 닫는다
    ("grpc.keepalive_timeout_ms", 20000),
    # 진행 중인 호출이 없어도 PING을 보낸다
    ("grpc.keepalive_permit_without_calls", 1),
]
channel = grpc.insecure_channel("localhost:50051", options=options)

keepalive PING 전송과 응답 없는 연결을 닫는 순서

서버 측 허용 범위

서버는 PING을 과도하게 자주 전송하는 클라이언트를 차단한다. 허용 간격보다 일찍 도착한 PING은 ping strike로 집계한다. 한도를 초과하면 서버는 too_many_pings 문구를 담은 GOAWAY(연결 종료를 알리는 HTTP/2 프레임)를 전송하고 연결을 닫는다.

이른 PING이 ping strike를 거쳐 GOAWAY에 이르는 흐름

서버 옵션 기본값 의미
grpc.http2.min_ping_interval_without_data_ms 300000 (5분) 허용하는 PING의 최소 간격
grpc.keepalive_permit_without_calls 0 호출이 없을 때 수신한 PING의 허용 여부
grpc.http2.max_ping_strikes 2 허용하는 ping strike 횟수

서버가 기본값을 사용하면 위 클라이언트 설정은 두 항목에서 서버 제한을 위반한다. PING 간격 60초는 최소 간격 5분보다 짧고, 호출이 없는 상태의 PING도 허용되지 않는다. 따라서 서버 설정도 함께 조정한다.

클라이언트 keepalive 설정과 서버 허용 범위의 비교

pythonserver.py
from concurrent import futures

import grpc

server = grpc.server(
    futures.ThreadPoolExecutor(max_workers=10),
    options=[
        ("grpc.http2.min_ping_interval_without_data_ms", 60000),
        ("grpc.keepalive_permit_without_calls", 1),
    ],
)

값 선택 기준

  • 클라이언트의 keepalive_time_ms는 서버의 최소 간격보다 짧지 않게 설정한다.
  • 1분보다 현저히 짧은 값으로 설정하지 않는다. 클라이언트 수가 많으면 PING만으로도 서버에 부하가 된다.
  • 서버 설정을 변경할 수 없으면 해당 서버의 허용 범위 내로 클라이언트 설정을 조정한다.
  • keepalive_permit_without_calls는 호출이 없는 동안에도 연결을 유지해야 하는 경우에만 활성화한다.