gRPC에서 Deadline 설정으로 타임아웃 제어하기
문제 상황
결제 서비스에서 주문 서비스를 gRPC로 호출하는 구조였는데, 주문 서비스가 느려지면 결제 서비스까지 타임아웃 없이 무한정 대기하는 문제가 발생했다. 트래픽이 몰릴 때 서비스 전체가 느려지는 연쇄 장애로 이어졌다.
Deadline 설정
gRPC는 HTTP/2 기반이라 Keep-Alive 연결이 유지되는데, 명시적으로 Deadline을 설정하지 않으면 타임아웃이 적용되지 않는다.
const grpc = require('grpc');
const client = new OrderServiceClient(
'order-service:50051',
grpc.credentials.createInsecure()
);
// Deadline 설정 (3초)
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
client.getOrder(
{ orderId: '12345' },
{ deadline: deadline },
(err, response) => {
if (err) {
if (err.code === grpc.status.DEADLINE_EXCEEDED) {
console.error('Request timeout');
}
return;
}
// 정상 처리
}
);
Context Propagation
더 중요한 건 상위 서비스에서 받은 Deadline을 하위 서비스 호출 시에도 전파하는 것이었다. 클라이언트가 설정한 타임아웃이 모든 호출 체인에 적용되어야 한다.
// 서버에서 받은 context의 deadline을 추출
function handleRequest(call, callback) {
const deadline = call.getDeadline();
// 다른 서비스 호출 시 동일한 deadline 전파
downstreamClient.someMethod(
request,
{ deadline: deadline },
callback
);
}
결과
- 장애 격리: 하나의 서비스 지연이 전체로 전파되지 않음
- P99 응답 시간 개선: 3초 이상 대기하던 요청들이 명확히 실패 처리됨
- 모니터링 가능: DEADLINE_EXCEEDED 에러로 타임아웃 추적 가능
REST API에서는 라이브러리 수준에서 타임아웃이 기본 설정되는 경우가 많지만, gRPC는 명시적으로 Deadline을 관리해야 한다는 점을 배웠다.