grpc-webを使うための準備


はじめに

こんにちは。こちらはトレタ Advent Calendar 2019 22日の記事です。(大幅遅刻で申し訳ありません)
トレタでは積極的にgRPCを採用しています。そこでgrpc-webを利用するための準備をまとめました。

gRPCとブラウザの関係

現在2019/12時点ではウェブブラウザではgRPCで通信することはできません。
そのため、gRPCとhttpを変換するための機構が必要となります。
候補として2つあり、
・ gRPCサービスにRestful-APIのリバースプロキシを追加するgrpc-gateway
・ http上でbinaryもしくはbase64encodeしたデータを送信して、envoyなどのproxyでgRPCに変換するgrpc-web

grpc-webとjavascript

grpc-webはgRPC for Web Clientsと謳われているようにgRPC自体をweb clientで扱うためにあります。
gRPCで利用されるデータのインターフェース構造はprotocol buffersで設定します。
protocol buffersはIDLのため各種言語用に変換をしなくてはいけません。

protocとprotoc-gen-grpc-web

protocol buffersを変換するためのprotocとgrpc-web用に生成するためのプラグインprotoc-gen-grpc-webを利用します。
こちら両方とも実行ファイルのためプラットフォーム別にファイルが異なっているので、管理上dockerで生成すると便利だと思います。

FROM node:12-buster

ARG APP_HOME=${APP_HOME:-/app}
RUN mkdir -p $APP_HOME

ARG GRPC_HOME=${GRPC_HOME:-/grpc}
RUN mkdir -p $GRPC_HOME
WORKDIR $GRPC_HOME

USER root

RUN npm i rimraf -g
RUN curl -L -O https://github.com/protocolbuffers/protobuf/releases/download/v3.11.2/protoc-3.11.2-linux-x86_64.zip
RUN curl -L -O https://github.com/grpc/grpc-web/releases/download/1.0.7/protoc-gen-grpc-web-1.0.7-linux-x86_64
RUN unzip protoc-3.11.2-linux-x86_64.zip && cp ./bin/protoc /usr/local/bin/. && chmod +x /usr/local/bin/protoc
RUN cp protoc-gen-grpc-web-1.0.7-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web && chmod +x /usr/local/bin/protoc-gen-grpc-web

WORKDIR $APP_HOME

protocのdockerをdocker-composeから利用します。

version: '3.7'
services:
  protoc:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - type: bind
        source: ./
        target: /app
    command: bash -c 'npm run protoc'

プロジェクトrootをbindしてnpmで実行するようにpackage.jsonにscriptを作成します。

"scripts": {
    "protoc": "npx rimraf ./grpc && mkdir -p ./grpc && cd ./proto && protoc *.proto -I. --js_out=import_style=commonjs:../grpc --grpc-web_out=import_style=typescript,mode=grpcwebtext:../grpc"
}

protocol buffersをprotoディレクトリに用意します、

syntax = "proto3";

message HelloRequest { string world = 1; }
message HelloResponse { string world = 1; }

service HelloService { rpc World(HelloRequest) returns (HelloResponse); }

これでdocker-compose run protocと実行すればgrpcディレクトリにjavascriptとtypescriptのファイルが生成されます。

.
├── Dockerfile
├── docker-compose.yml
├── grpc
│   ├── HelloServiceClientPb.ts
│   ├── hello_pb.d.ts
│   └── hello_pb.js
├── index.ts
├── node_modules
│   └── grpc-web
│       ├── README.md
│       ├── index.d.ts
│       ├── index.js
│       └── package.json
├── package-lock.json
├── package.json
└── proto
    └── hello.proto

利用方法

まずはgrpc-webをインストールします。

npm install -S grpc-web

まずは、service clientを呼び出します。

const client = new HelloServiceClient("https://gprc-server");

次にリクエストを作成します。

リクエストにはprotoで設定したworldにstringで設定します。

  const req = new HelloRequest();
  req.setWorld("world");

service HelloServiceで設定したrpc Worldにリクエストとmeta dataを渡せばcallbackで返ってきます。

client.world(req, {helloAuth: 'secret string'}, (err, res) => {
    if (err) {
      console.error(err.message);
    } else {
      console.log(res.getWorld());
    }
  });

これで、クライアント側でgrpcを利用することができました。

さいごに

protocol buffersからブラウザで通信できるまでをやってみました。
間違っているところやもっといい方法がありましたらご指摘などお待ちしております。

まだenvoyの設定やgRPCサーバの用意などやることはあります。
こちらもご要望がありましたらまた別途かけたらなと思います。
ありがとうございました。