gRPC 스트리밍으로 실시간 로그 전송 구현하기
문제 상황
여러 마이크로서비스에서 발생하는 로그를 중앙 모니터링 시스템으로 실시간 전송해야 했다. 기존에는 REST API로 배치 전송했지만, 장애 발생 시 즉각 대응이 어려웠다.
WebSocket도 고려했으나 로드밸런서 설정이 복잡하고, 이미 서비스 간 통신에 gRPC를 사용 중이라 gRPC 스트리밍으로 통일하기로 결정했다.
gRPC Server Streaming
proto 파일 정의는 간단했다.
service LogService {
rpc StreamLogs(LogRequest) returns (stream LogEntry) {}
}
message LogRequest {
string service_name = 1;
string level = 2;
}
message LogEntry {
string timestamp = 1;
string level = 2;
string message = 3;
}
Node.js 서버 구현:
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
function streamLogs(call) {
const { service_name, level } = call.request;
const interval = setInterval(() => {
const log = {
timestamp: new Date().toISOString(),
level: level,
message: `Log from ${service_name}`
};
call.write(log);
}, 1000);
call.on('cancelled', () => {
clearInterval(interval);
});
}
클라이언트에서는 stream을 받아 처리한다.
const call = client.streamLogs({
service_name: 'api-server',
level: 'error'
});
call.on('data', (log) => {
console.log(`[${log.timestamp}] ${log.level}: ${log.message}`);
});
call.on('end', () => {
console.log('Stream ended');
});
결과
HTTP/2 기반이라 멀티플렉싱이 자동으로 처리되고, 백프레셔도 내장되어 있어 안정적이었다. Protobuf 덕분에 JSON 대비 페이로드도 30% 정도 줄었다.
다만 디버깅이 REST보다 까다로워서 grpcurl, grpc-health-probe 같은 도구를 적극 활용했다. 프로덕션 환경에서 3주간 운영 중인데 연결 끊김 없이 잘 동작하고 있다.