RPC 알아보기 3 - gRPC(java) 예제
Intro
이번 게시물에서는 gRPC와 Http의 속도 차이를 비교하려고 한다.
전체적인 예시 코드는 여기를 참고했다.
gRPC
RPC의 가장 큰 이점은 애플리케이션 레이어의 코드에 있는 것 같다. stub 객체에 마치 로컬 함수를 호출하는 것처럼 작성하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HelloServiceClient {
private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub;
public HelloServiceClient(String host, int port) {
ManagedChannel managedChannel = ManagedChannelBuilder
.forAddress(host, port)
.usePlaintext()
.maxInboundMessageSize(Integer.MAX_VALUE)
.build();
this.blockingStub = HelloServiceGrpc.newBlockingStub(managedChannel);
}
public HelloResponse sayHelloWithBlocking(HelloRequest request) {
HelloResponse helloResponse = blockingStub.sayHello(request); // 리모트 서버의 프로시저 호출
return helloResponse;
}
}
RPC에서는 middleware를 stub 이라고 부른다. 연결할 리모트 서버의 정보를 넣은 ManagedChannel
을 만든 후에 stub을 제작하면 된다.
RPC는 기본적으로 synchronization 이다. 하지만 그 구현체에 따라서 Asynchronous를 지원하기도 하는데, gRPC도 이를 지원한다. 여러 통신 기법에 대해서는 여기에서 확인할 수 있다.
HTTP 프로토콜과 비교
HTTP 서버는 com.sun.net.httpserver
를 이용해서 작성했다.
1
2
3
4
5
6
7
8
9
InetSocketAddress address = new InetSocketAddress(4321);
HttpServer httpServer = HttpServer.create(address, 0);
httpServer.createContext("/", (exchange) -> {
InputStream inputStream = exchange.getRequestBody();
byte[] bytes = inputStream.readAllBytes();
// ..response..
});
httpServer.start();
시나리오: Client는 byte[]를 서버에 전송한다. 서버는 받은 바이트의 크기를 담아 response 한다. HTTP는 post 메서드를 사용한다.
속도 비교
gRPC는 unary, blocking 을 사용하였고, HTTP는 http/1.1, post, blocking 을 사용했다.
또한 네트워크가 서로 다른 WAN 환경이고, 아래 결과는 10회 전송했을 때의 response time 합이다.
결과 분석
- (당연하지만) 네트워크 환경에 따라 응답 속도의 편차가 충분히 존재한다.
- 바이트 수가 작을 때는 gRPC의 이점인 protobuf과 HTTP/2을 충분히 활용하지 못하므로 큰 이점은 없다.
- 바이트 수가 커짐에 따라 protobuf의 장점 때문에 속도 차이가 유의미하게 생긴다.
아쉬운 점은, HTTP/2의 이점을 살리지 못했다는 점이다. 단 1건의 unary만 사용했기 때문에 stream의 이점을 살리지 못했다. 또한 body를 byte[] 로 설정했기 때문에 압축면에서 압도적인 차이를 만들지 못한 것 같다.
다음 게시물에서는 stream 사용, http의 json 형식 전송 등 다양한 환경에서 테스트해보자.