구글에서 개발한 RPC 프레임워크로, 네트워크를 통해서 다른 시스템의 함수를 호출할 수 있게 해 준다. 클라이언트 애플리케이션은 다른 머신의 서버 애플리케이션의 함수를 로컬 함수처럼 호출해 사용할 수 있다.
클라이언트와 서버는 다양한 환경으로 구성되어 실행하고 통신할 수 있다. Java 로 gRPC 서버를 만들고 Go, Python 등으로 gRPC 클라이언트를 만들 수 있는 등, gRPC 에서 지원하는 모든 언어로 다양하게 환경을 구성할 수 있다.
로컬에서 함수를 호출하듯이, 다른 서버의 함수를 호출할 수 있게 하는 기술.
예를 들어, A서버에서 B서버의 calculate(a, b)
를 호출하면, 네트워크를 통해 통신해서 결과를 받을 수 있다.
gRPC는 데이터를 직렬화 및 역직렬화 하기 위해 Protocol Buffers 라는 포맷을 사용한다. Protocol Buffers 는 JSON이나 XML보다 더 빠르고 작은 크기로 데이터를 처리한다.
.proto
파일로 메세지와 서비스를 정의하면 정의한 내용을 바탕으로 프로토콜 버퍼 컴파일러(protoc
)를 통해 각 언어에 맞게 사용할 수 있는 코드가 자동으로 생성된다.
Proto3 버전은 더 많은 언어 지원과 새로운 기능이 추가되었다.
일반적으로 현재 기본 프로토콜 버퍼 버전인proto2
를 사용할 수 있지만, gRPC 사용 시proto3
를 사용하는 것을 추천한다고 안내하고 있다.proto3
는 gRPC에서 지원하는 모든 언어를 사용할 수 있고,proto2
와proto3
간의 통신에 대한 호환성 문제를 피할 수 있다.
gRPC는 HTTP/2
프로토콜을 사용해 기존 HTTP/1.1
보다 빠른 통신과 효율적인 데이터 전송을 제공한다.
HTTP/1.1
은 하나의 요청-응답만 처리할 수 있다. 요청마다 별도의 TCP 커넥션을 열어야 하고, 추가 요청은 이전 요청이 끝나야 처리할 수 있다.
HTTP/2
는 다중화(멀티플렉싱)를 지원해 하나의 TCP 연결로 여러 요청과 응답을 동시에 처리할 수 있다. 또 헤더 압축, 서버 푸시, 스트림 지원 등의 기능을 통해 대역폭을 효율적으로 사용하고 지연 시간을 줄인다.
멀티플렉싱
하나의 TCP 연결로 여러 요청과 응답을 주고 받을 수 있는 기술.
만약 a, b, c 세 개의 요청을 보냈다면 a 가 끝날 때까지 b, c가 기다리지 않고 병렬 처리된다.
지연 시간 감소와 네트워크 효율성을 증가시킬 수 있다.
헤더 압축
HTTP/1.1 은 요청마다 헤더를 전송해 중복 데이터가 많다. HTTP/2
는 HPACK
방식으로 헤더를 압축해 전송 데이터를 줄인다.
데이터 전송량이 감소하며 속도가 향상된다.
스트림 지원
스트림
이라는 독립적인 데이터 채널을 통해서 요청과 응답을 처리한다.
각 스트림은 순서와 우선순위를 가지고 있어 네트워크 자원을 효과적으로 사용한다.
요청이 많더라도 네트워크 충돌 없이 유연한 처리가 가능하다.
서버 푸시
클라이언트가 요청하지 않아도 서버가 데이터를 클라이언트에게 전송할 수 있는 방식. 클라이언트가 추가 요청을 보내기 전에 필요한 리소스를 받을 수 있어 지연 시간이 감소하고 불필요한 요청을 줄여 네트워크의 자원을 절약한다.
ex) 클라이언트가 웹 페이지의 html 을 요청하면 서버가 html 뿐만 아니라 해당 페이지에 필요한 css, javascript 파일도 미리 전송할 수 있다. 클라이언트는 필요한 파일을 기다릴 필요가 없어 페이지 로드 시간이 단축된다.
단일 요청 및 응답
일반적인 함수 호출처럼 요청에 대한 응답을 받는다.
rpc SayHello(HelloRequest) returns (HelloResponse);
서버 스트리밍
클라이언트가 요청하면 서버에서 여러 응답을 스트리밍으로 보낸다.
클라이언트가 요청을 보내면 서버가 여러 개의 응답을 순차적으로 스트리밍한다. 개별 RPC 호출 내에서 메세지 순서를 보장하며 클라이언트는 더이상 메세지가 없을 때까지 반환된 스트림을 읽는다.
ex) 영화 스트리밍 서비스에서 클라이언트가 한번 요청하면 서버는 영화 데이터를 지속적으로 보낸다.
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
클라이언트 스트리밍
클라이언트가 여러 데이터를 보내고 서버는 단일 응답을 보낸다. 개별 RPC 호출 내에서 메세지 순서를 보장한다.
ex) 클라이언트가 센서 데이터를 실시간으로 서버에 전송한다.
rpc LotsOfReplies(stream HelloRequest) returns (HelloResponse);
양방향 스트리밍
클라이언트와 서버가 데이터를 동시에 주고 받는다. 서버는 응답하기 전에 모든 클라이언트 메세지를 수신할 때까지 기다리거나, 메세지를 읽고 쓰기를 번갈아 가거나, 읽기와 쓰기를 조합할 수 있다. 각 스트림의 메세지 순서는 유지된다.
rpc BidHello(stream HelloRequest) returns (stream HelloResponse);
스트리밍
데이터를 한번에 전송하는 대신, 일정한 간격으로 나누어 전송하는 방식
클라이언트와 서버 간 통신을 간단하게 만들어주는 자동 생성되는 코드로,
Stub
을 통해 클라이언트는 원격 서버의 함수를 로컬 함수처럼 호출할 수 있고 네트워크 관련한 복잡한 처리를 직접 구현하지 않아도 된다.
Stub
은 클라이언트와 서버 간의 중간 인터페이스 역할을 한다.
Stub
Stub
을 통해서 네트워크 요청 및 응답 처리를 자동으로 수행한다.Stub
내부에서 처리된다.Stub
.proto
파일 작성 - 서비스 정의 및 요청/응답 메세지 구조 작성syntac = "proto3";
message PersonRequest {
int32 id = 1;
}
message PersonResponse {
string name = 1;
int32 id = 2;
string email = 3;
}
service PersonService {
rpc GetPerson (PersonRequest) returns (PersonREsponse);
}
protoc
를 사용해 클라이언트와 서버의 Stub
코드 자동 생성PersonServiceGrpc.PersonServiceBlockingStub
PersonServiceGrpc.PersonServiceImplBase
클라이언트 Stub
호출
Stub
을 통해 서버의 함수를 호출하고, 요청 데이터를 직렬화 해 네트워크로 전송
서버 Stub
처리
클라이언트로부터 요청 데이터를 받아 디코딩, 실제 구현된 함수 로직을 수행하고 결과를 반환
종류 | 동작 |
---|---|
Blocking Stub | 동기 방식으로 동작. |
Async Stub | 비동기 방식으로 동작. 서버의 응답을 기다리지 않고 요청을 보낸 후, 콜백 함수를 통해 결과 처리 |
Future Stub | 비동기 호출의 결과를 Future 객체로 반환.클라이언트가 나중에 결과 확인 가능 |
gRPC는 Protocol Buffers
와 HTTP/2
덕분에 더 빠르고 효율적이다. Java, Python, Go, C++ 등 다양한 언어로 사용 가능하며, .proto
파일로 자동화된 코드로 클라이언트와 서버 코드를 쉽게 생성한다. 또 단일 요청부터 양방향 스트리밍까지 다양한 통신을 지원하고 있어 각각의 서비스 요구사항에 맞게 설계할 수 있어 유연성이 높다.
하지만 gRPC를 활용하기 위한 추가 학습이 필요하다. 단순 CRUD 서비스에서는 과도한 도입일 수 있다. 브라우저는 웹소켓이나 RESTful API 를 통해야 사용이 가능한데, 브라우저 클라이언트와의 직접 통신이 필요한 경우 REST보다 불편할 수 있다. 또 grpccurl
, Postman
등의 추가 도구 없이는 디버깅이 어렵다.
기능 | RESTful API | gRPC |
---|---|---|
통신 프로토콜 | HTTP/1.1 | HTTP/2 |
데이터 형식 | JSON | Protocol Buffers (바이너리 형식) |
성능 | 텍스트 기반으로 상대적으로 느림 | 바이너리 기반으로 빠르고 효율적 |
코드 생성 | 직접 작성 필요 | .proto 파일로 자동 생성 |
다중 언어 지원 | 언어간 표준 없음 | 다양한 언어에서 동일한 방식으로 사용 가능 |
유연성 | 단일 요청-응답 | 스트리밍, 양방향 통신 등의 다양한 방식 지원 |
gRPC 공식 FAQ에 의하면, gRPC가 REST와 비교해 아래와 같이 기술하고 있다.
Why is gRPC better/worse than REST?
gRPC largely follows HTTP semantics over HTTP/2 but we explicitly allow for full-duplex streaming. We diverge from typical REST conventions as we use static paths for performance reasons during call dispatch as parsing call parameters from paths, query parameters and payload body adds latency and complexity. We have also formalized a set of errors that we believe are more directly applicable to API use cases than the HTTP status codes.
gRPC는 HTTP/2
기반으로 동작하며, HTTP
의 기본적인 동작 원칙을 따른다.
하지만 gRPC는 풀-듀플렉스 스트리밍(클라이언트와 서버가 동시에 데이터를 주고받을 수 있는 기술, 양방향 스트리밍) 을 명시적으로 지원한다.
gRPC는 정적 경로(static path)를 사용한다. 이는 Call Dispatch 의 성능을 높이기 위해서다.
Call Dispatch
클라이언트 요청을 서버에서 적절한 서비스나 메서드로 매핑하는 방식
- REST: 동적 경로를 파싱하고 경로 및 매개변수에 따라 호출할 핸들러를 찾는다.
- gRPC: 정적 경로를 사용해 추가적인 파싱 과정 없잉 서비스와 메서드를 즉시 매핑한다.
정적 경로는 고정된 URL 형식을 사용한다. 경로의 구조가 일정하고 요청마다 변하지 않는다. gRPC는 서비스 호출 경로를 .proto
파일에 정의된 고정 경로를 사용한다. 서비스 이름과 메서드 이름으로 구성되며 요청 데이터는 페이로드로 전달된다.
service Calculator {
rpc Add (AddRequest) returns (AddResponse);
}
// 경로 : /Calculator/Add
동적 경로는 경로에 변수나 매개변수를 포함할 수 있다.
/users/{id}/orders/{orderId}
REST는 경로, 쿼리 매개변수, 페이로드에서 데이터를 파싱하는데, 이러한 방식은 지연 시간과 복잡성을 증가시킬 수 있어 gRPC는 REST의 관례를 따르지 않는다.
gRPC는 API 사용 사례에 더 적합하다고 판단한 표준화된 에러 세트를 정의해 사용한다.
REST에서 일반적으로 사용하는 HTTP 상태 코드보다 더 직관적이고 API에 적합한 에러 코드 체계를 제공한다.
REST 의 HTTP 상태 코드는 gRPC의 에러 코드와 아래와 같이 매핑할 수 있다.
400 Bad Request
-> INVALID_ARGUMENT
401 Unauthorized
-> UNAUTHENTICATED
403 Forbidden
-> PERMISSION_DENIED
404 Not Found
-> NOT_FOUND
500 Internal Server Error
-> INTERNAL
특히 HTTP 상태 코드 500
은 gRPC에서 INTERNAL
, UNAVAILABLE
, DATA_LOSS
등으로 더 명확히 구분할 수 있다.
gRPC 는 낮은 지연 시간과 높은 확장성으로 분산 시스템 및 MSA 구조에서 효율적으로 활용할 수 있다. 단순한 구조의 시스템에서 적용하기에 과도할 수 있지만, 여러 서비스 간 통신이 빈번하고 대규모 서비스 간의 통신을 효율적으로 다루기 위해서 다양한 설계 방식을 적용하는 데에 최적화 할 수 있는 기술로 활용할 수 있다.
실제 업무에 도입해서 사용했을 때 크게 와닿았던 부분은 엄격한 인터페이스 관리가 가능하다는 점이었다. 서버와 클라이언트가 .proto
파일에 정의된 내용이 동일하지 않으면 (필드 번호가 변경되거나, 데이터 타입이 변경되는 등) Stub 메서드 호출 시점에 에러가 발생한다. 당시에 프로젝트를 진행하면서 배포와 픽스를 반복하며 속도감 있게 진행했는데, 반영된 변경사항을 조금 더 빠르게 인지할 수 있었고 하나의 파일로 정의된 작업 내용을 파악할 수 있어 업무 소통에 유연함을 더할 수 있었다. 처음 .proto
파일을 작성하는 것이 처음에는 꽤나 귀찮(...)았지만, 활용에 익숙해지면서 gRPC 동작 방식과 핵심 개념, 이점을 이론적으로 이해하고 기술의 목적에 기반해 설계에 맞게 적용할 수 있는 스킬을 갖추기 위해 정리하였다.