gRPC-web 삽질기

Kyu·2020년 3월 11일
14
post-thumbnail

gRPC-web 브라우저 적용 삽질기

Javascript 기반으로 브라우저에서 gRPC를 사용하려고 하였지만, 적용하지 못한 내용에 대한 기록입니다.
gRPC에 대해서 간략히 설명하고 gRPC 라이브러리를 이용하여 node.js 기반의 Application을 만드는 과정,
그리고 브라우저와 back-end 사이에 grpc-web을 사용하여 직접 통신할 수 없는 이유를 작성하였습니다.


gRPC

gRPC는 Google이 개발한 고성능 오픈소스 범용 RPC 프레임워크입니다. gRPC에서 클라이언트 애플리케이션은 다른 머신의 서버 애플리케이션에 있는 메소드를 로컬 객체처럼 직접 호출할 수 있으므로 분산 애플리케이션 및 서비스를 더 쉽게 만들 수 있습니다.

RPC는 네트워크 상 원격에 있는 서버의 서비스를 호출하는데 사용되는 프로토콜로 IDL(Interface Definition Language)로 인터페이스를 정의한 후 이에 해당하는 Skeleton 과 Stub 코드를 통해 프로그래밍 언어에서 호출해서 사용하는 방식입니다. 최근에는 HTTP를 활용한 RESTful이 많이 활용되어서 RPC는 거의 사용이 되지 않으나 요청/응답을 위한 규약이 명시적이지 않다는 단점으로 인해 다시 RPC의 방식을 채용한 프레임워크들이 나오기 시작했습니다.

gRPC는 자바, C/C++ 뿐만 아니라 Node.js, Python, Ruby, PHP, Go, Android 기반, Objective-C 등 다양한 언어들을 지원함으로 서버간 뿐만이 아니라 클라이언트 어플리케이션이나 모바일 앱에서도 사용이 가능합니다.

gPRC 구조

gRPC는 기존 RPC와 마찬가지로 IDL(Interface Definition Language)을 기반으로 원격으로 프로시저를 호출할 수 있는 방법을 제공합니다. 기본적으로 gRPC는 서비스 인터페이스와 페이로드 메시지의 구조를 표현하기 위해 Protocol Buffers를 IDL로 사용합니다.
JSON등 다른 직렬화 방식을 사용할 수도 있습니다.

Protocol Buffers는 구조화된 데이터를 직렬화하여 압축하기 때문에 JSON, XML에 비해 전송 속도가 빠릅니다

gRPC 서비스

gRPC를 사용하면 아래 네 가지 서비스를 사용할 수 있습니다.

  • Unary RPC (단일 호출)
  • Server streaming RPC (서버 스트리밍)
  • Client streaming RPC (클라이언트 스트리밍)
  • Bidirectional streaming RPC (양방향 스트리밍)
/* gRPC 구현을 위한 proto 파일 작성 예시 */
/* hello.proto */
//
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}
  • Unary RPC
    클라이언트가 단일 함수를 서버에 보내고 단일 함수 호출과 같은 단일 응답을받는 단항 RPC.
  rpc SayHello(HelloRequest) returns (HelloResponse){}
  • Server streaming RPC
    클라이언트가 서버에 요청을 보내고 스트림을 가져 와서 일련의 메시지를 다시 읽는 서버 스트리밍 RPC.
    클라이언트는 더 이상 메시지가 없을 때까지 리턴 된 스트림에서 읽음.
  rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){}
  • Client streaming RPC
    클라이언트가 일련의 메시지를 작성하고 제공된 스트림을 사용하여 다시 서버로 보내는 클라이언트 스트리밍 RPC.
    클라이언트가 메시지 쓰기를 마치면 서버가 메시지를 읽고 응답을 반환 할 때까지 대기.
  rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {}
  • Bidirectional streaming RPC
    양쪽에서 읽기 / 쓰기 스트림을 사용하여 메시지를 주고 받는 양방향 스트리밍 RPC.
    두 스트림은 독립적으로 작동하므로 클라이언트와 서버는 원하는 순서대로 읽고/쓰기가 가능.
  rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){}

gRPC-WEB

gRPC-WEB은 gRPC를 브라우저에서 사용가능하도록 브라우저 지원을 추가한 스펙입니다. Google 팀과 Improbable 팀이 2016년부터 독립적으로 구현을 진행하다가 새로운 프로토콜 스펙의 정의를 위해 함께 모여서 만들게 되었습니다.
gRPC-WEB 사양은 HTTP/1.1 과 HTTP/2를 모두 지원하지만, 현재 브라우저 API가 HTTP/2 엑세스를 지원하지 않기 때문에 gRPC-WEB을 사용하여 브라우저에서 서버로 직접 통신을 할 수는 없습니다. 하지만 응답 변환 Proxy 서버를 통해 요청 및 응답을 브라우저가 처리할 수 있도록 변환 할 수 있습니다.
Google 팀과 Improbable 팀은 독립적으로 각각의 저장소에 다른 방식으로 구현하였으며, 스펙도 완전히 준수하지는 않았습니다.

Improbable 팀은 Typescript 기반으로 @improbable-eng/grpc-web을 구현하였으며, Google팀은 Google의 클로저 라이브러리 베이스로 Javascript 기반의 grpc-web을 구현하였습니다.

Google의 grpc-web은 grpcwebtext 모두와 함께 사용하는 경우만 서버측 스트리밍을 지원합니다.
Improbable 팀의 양방향 스트리밍은 실험적인 기능이므로 상용 제품에 사용하기에는 문제가 있을 수 있습니다.

Google에서는 Envoy Proxy를 공식적인 기본 솔루션으로 홍보하고 있으며, Improbable 팀의 프록시는 실험적이라고 얘기하고 있습니다.

grpc node.js 애플리케이션 작성

grpc 라이브러리를 사용하는 node.js 애플리케이션 작성 예제입니다. (grpc-web 동작 안함...😭)

1) proto file 작성

protocol buffers를 사용을 위한 IDL을 작성합니다.

// helloworld.proto
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

2) protoc 컴파일러를 이용하여 .proto file을 컴파일하면, 'helloworld_pb.js', 'helloworld_grpc_pb.js' 파일이 생성됩니다.

$ protoc --proto_path=.\hello.proto --js_out=import_style=commonjs,binary:.\dist --plugin=protoc-gen-grpc-web=.
\plugin\protoc-gen-grpc-web.exe --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.\dist

3) 두개의 파일을 import 하여 서버 애플리케이션을 작성

/* Server */
/* : node server.js */

var messages = require('./dist/helloworld_pb');
var services = require('./dist/helloworld_grpc_pb');

var grpc = require('grpc');

function sayHello(call, callback) {
  var reply = new messages.HelloReply();
  reply.setMessage('Hello ' + call.request.getName());
  callback(null, reply);
}

function main() {
  var server = new grpc.Server();
  console.log('Hello Server');
  server.addService(services.GreeterService, { sayHello: sayHello });
  server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

4) 두개의 파일을 import 하여 클라이언트 애플리케이션을 작성

/* Client */
/* : node client.js */

var messages = require('./helloworld_pb');
var services = require('./helloworld_grpc_pb');

var grpc = require('grpc');

function main() {
  var client = new services.GreeterClient('localhost:50051',
  grpc.credentials.createInsecure());
  
  var request = new messages.HelloRequest();
  var user;
  
  if (process.argv.length >= 3) {
    user = process.argv[2];
  } else {
  	user = 'world';
  }
  
  request.setName(user);
  client.sayHello(request, function(err, response) {
    console.log('Greeting:', response.getMessage());
  });
}

main();

결론

gRPC의 사용은 HTTP/2 기반에서 얻을 수 있는 헤더 압축, 단일 TCP Connection의 병렬 처리, 보안기능등으로 많은 성능상의 이점을 얻을 수 있습니다. 또한 Protocol Buffers를 사용하므로 데이터의 압축율을 크게 향상시킬 수 있습니다. 이러한 이점으로 gRPC는 아래와 같은 시나리오에서 적합하다고 합니다.

  • 마이크로 서비스: grpc는 대기 시간이 짧고 처리량이 높은 통신을 위해 설계 되었습니다. gRPC는 효율성이 중요한 경량 마이크로 서비스에 적합 합니다.
  • gRPC: 지점 간 실시간 통신 은 양방향 스트리밍을 위한 뛰어난 지원 기능을 제공 합니다. gRPC 서비스는 폴링을 사용 하지 않고 실시간으로 메시지를 푸시할 수 있습니다.
  • Polyglot 환경: grpc 도구는 널리 사용 되는 모든 개발 언어를 지원 하며, grpc를 다중 언어 환경에 적합 하 게 선택할 수 있습니다.
  • 네트워크 제한 환경: grpc 메시지는 경량 메시지 형식인 Protobuf를 사용 하 여 serialize 됩니다. GRPC 메시지는 항상 해당 하는 JSON 메시지 보다 작습
    니다.

다만, 데이터가 바이너리 인코딩되므로 사람이 읽기가 어렵고, 브라우저에 지원이 제한적이고 프로토콜 변환을 위한 프록시 서버가 필요합니다.


참고

JavaScript Generated Code: https://developers.google.com/protocol-buffers/docs/reference/javascript-generated
protoc 컴파일러: https://github.com/protocolbuffers/protobuf/releases
protoc-gen-grpc-web 플러그인: https://github.com/grpc/grpc-web/releases

https://developers.google.com/web/fundamentals/performance/http2/?hl=ko
https://docs.microsoft.com/ko-kr/aspnet/core/grpc/comparison?view=aspnetcore-3.
https://medium.com/@goinhacker/microservices-with-grpc-d504133d191d
https://medium.com/@aravindhanjay/a-todo-app-using-grpc-web-and-vue-js-4e0c18461a3e
https://grpc.io/blog/grpc-web-ga/
https://grpc.io/blog/state-of-grpc-web/
https://github.com/grpc/grpc-web/issues/
https://github.com/grpc/grpc-web/issues/
https://grpc.github.io/grpc/node/grpc.Server.html
https://www.freecodecamp.org/news/how-to-use-grpc-web-with-react-1c93feb691b5/
https://arisu1000.tistory.com/
https://medium.com/@goinhacker/grpc-more-practical-story-87765af3b21d
https://morioh.com/p/ae48b33d10a

4개의 댓글

comment-user-thumbnail
2020년 8월 19일

안녕하세요. 혹시 브라우저와 back-end 사이에 grpc-web을 사용하여 직접 통신할 수 없는 이유를 알 수 있을가요...?

1개의 답글
comment-user-thumbnail
2021년 6월 8일

잘 읽고 갑니다 ;D
오늘 방송하면서 라이브코딩 하면서, gRPC에 대해 빠르게 이해할 수 있어서 좋았어요!

1개의 답글