gRPC WebがGAになったのでそろそろ理解してみる

2018/10/23に「gRPC-Web is Generally Available」というタイトルでgRPC-WebがGAになった事がgrpc公式ブログで発表されました。
https://grpc.io/blog/grpc-web-ga
翌日にCNCFのブログでも「gRPC-Web is going GA」というタイトルで同等の内容が公開されました。
https://www.cncf.io/blog/2018/10/24/grpc-web-is-going-ga/

今までブラウザからgrpcが使えるgRPC-Webの存在は知っていましたが、ちまたで実際に利用しているというのもあまり聞かなかった為ほとんど気にしていませんでした。
この度GAになったのをきっかけにエコシステムができあがってくれば間違いなくパラダイムシフトになると思いちゃんと検証してみました。

## この記事で書く事

* gRPC-Webのアーキテクチャや周辺モジュールについてざっくり
* 実際に自分のローカル環境でgRPC-Webを試してみる方法について

## この記事で書かない事

* gRPCおよびprotocol buffersに関する基本的な事

## アーキテクチャ

まずどのような仕組みでgRPC-WebからgRPCサーバに接続されるかを確認してみます。

公式ブログの記事では以下の図で説明しています。

![grpc-web-arch.png](https://qiita-image-store.s3.amazonaws.com/0/39177/6fd19e3e-edd9-fadd-e8f7-a2a2171ff4f8.png)

今までは右の図のようなアーキテクチャだったけど今後は左の図のようにできるよっていう話です。

自分はこの図をパッとみた時にブラウザから直接gRPCサーバにアクセスできるのかと思いました。つまりブラウザから「`grpc://`」的なURLスキームを使ってアクセスする感じを想像しましたが、そんなURLスキームはブラウザに標準実装されていません。

記事を読めばわかりますが「gRPC-Web app」と「GRPC」の間にはgRPC Web対応のプロキシが必要という事がわかりました。
ブラウザとこのプロキシとはhttp通信を行います。（つまり `http(s)://` を使います）



ちなみにこの記事を書いている間にちょうどCNCFのブログを書いた人が以下の記事を公開したので確認してみてみたところやはり**gRPC Web対応のプロキシが必要**だという事がわかりました。

https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880

以下、その部分の抜粋です。

> 私は以前、gRPC-WebクライアントではgRPCコールをバックエンドサービスに「直接」呼び出すことができると述べました。それは事実ではない。 gRPC-Webでは、依然としてgRPCに優しい呼び出しに変換する必要がありますが、その役割はgRPC-Webをサポートし、デフォルトのサービスゲートウェイとして機能するEnvoyによって満たされています。(Google翻訳)

認識に誤りはなかったようです。
正しくは以下のようになるとの事です。

![1_XzL4BnES1u_DXz3qXq73xQ.png](https://qiita-image-store.s3.amazonaws.com/0/39177/eaecbcab-4b74-7df3-a014-f3a664a32599.png)



## gRPC-Web対応プロキシ

gRPC-Web対応プロキシは現状以下が公式サポートされているみたいです。

* envoy [HTTP Filter - gRPC-Web](https://www.envoyproxy.io/docs/envoy/latest/configuration/http_filters/grpc_web_filter)
* nginx [GRPC gateway](https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/nginx)

ただしnginxの場合はデプロイが多少面倒なのでnginxにこだわる必要がなければ現状は**envoyを使うのが一番手っ取り早い**です。昨今のサービスメッシュの流れでenvoyがかなり知名度を上げてきてますね。

なお、ゆくゆくはプロキシなしで言語レベルでgRPC-Webに対応する可能性もあるようです。

非公式には以下があります。

* [grpcweb](https://github.com/improbable-eng/grpc-web/blob/master/go/grpcweb) (Go package) 
* [grpcwebproxy](https://github.com/improbable-eng/grpc-web/blob/master/go/grpcwebproxy) (Go-based stand-alone reverse proxy) 

上記は後述するgrpc-web-clientのクライアントライブラリをつくっている[Improbable](https://improbable.io/)という会社が開発しているみたいです。
grpcwebはgoのアプリケーションに組み込んで使うタイプで、grpcwebproxyはenvoyのgRPC-Webフィルタみたいなものだと思っておけば良さそうです。

## gRPC-Webのクライアントライブラリ

今回公式に発表されたgrpc-webとは別に非公式にgrpc-web-clientというものがあり2017年にオープンソースで公開されています。よって現状は2つ選択肢があるという事を覚えておいた方がいいと思います。

* [grpc-web](https://github.com/grpc/grpc-web)　 ※公式
* [grpc-web-client](https://github.com/improbable-eng/grpc-web)

grpc-web-clientは非公式と言いたいところですがprotocol buffersのjavascriptコンパイラで生成されるコードにこのライブラリがインポートされているのでこっちも公式みたいなものです。

確認したところgrpc-web-clientの方が機能的に進んでいるイメージがありますが、grpc-webに積極的にフィードバックされていくようです。

機能的な差異は以下の通りです。

| 項目 | grpc-web | grpc-web-client |
|-----------------|-----------------|-----------------|
| 通信方法| XHR(今後Fetchサポート予定?)        | Fetch(XHRフォールバックあり)|
| Typescript サポート | :o: | :o: |
| Node.js サポート | :heavy_multiplication_x: | :o: |
| サーバサイドストリーミング | :o: | :o: |
| クライアントサイドストリーミング | :heavy_multiplication_x: | :heavy_multiplication_x: |

クライアントサイドストリーミングは[Streams API](https://www.w3.org/TR/streams-api/)がブラウザに実装するまで実現ができないとの事です。

それぞれの使い方（コード生成方法)は以下の通りです。

### grpc-web

protocol buffersコンパイラのオプションとして `protoc-gen-grpc-web` があります。
ただし、バイナリが提供されていないっぽいので自力でmakeする必要があります。

詳細は以下に記載されています。

https://github.com/grpc/grpc-web#code-generator-plugin

grpc-webを使う場合はこのコンパイラで生成されたコードをインポートして使うかたちになります。

### grpc-web-client

protocol buffersコンパイラにデフォルトで入っているjavascript用コンパイラを使うとこのモジュールがインポートされます。Typescriptでも使えるようにするには別途 `ts-protoc-gen` をnpmでインストールしておく必要があります。

詳細は以下に記載されています。

https://github.com/improbable-eng/ts-protoc-gen

grpc-web-clientを使う場合はこのコンパイラで生成されたコードをインポートして使うかたちになります。

こう考えるとgoogleに頼らずgRPC Webの道を切り開いてきた[Improbable](https://improbable.io/)というは会社はすごいですね。ロンドンに本社があるゲームエンジンの開発会社みたいです。

## ローカル環境で試してみる

[公式のGithub](https://github.com/grpc/grpc-web)にもdocker-composeで確認できる手順が記載されていますが、いろいろなケースで自分なりに確認したかったので、独自に組んでみました。

ここにいろいろ書こうかと思いましたがGithubにあげましたので手順はREADMEみて下さい。
※まだサーバサイドストリーミングとかの検証はしていないので今後対応していきたいと思います。

https://github.com/betchi/grpc-web-sandbox

プロダクションで利用する場合は先頭にAPI Gatewayなり、リバプロなり入れると思うのでそういったケースもふまえていくつかのケース別に確認できます。
grpc-webとgrpc-web-clientの両方で確認できますし、http/1.1およびhttp/2での確認も可能です。

docker-composeを使って起動させて、ブラウザでアクセスしてテスト確認する感じになります。
（nginxのGRPC gatewayモジュール等のビルドも含めると結構時間がかかるので、そんなにさくっと動くわけではありません。ご注意下さい。）

テスト確認画面のキャプチャはこんな感じです。

<img width="1230" alt="screenshot.png" src="https://qiita-image-store.s3.amazonaws.com/0/39177/532f1f69-1ed9-5dfe-3598-a7844f3cc27c.png">


画面だけでは成功か失敗かしかわからないのでブラウザのデベロッパーツールを使ってネットワークパネルみるといいと思います。
API Gateway単体のオープンソースとしては個人的に好きな[kong](https://konghq.com/)を使っていますが、「KUBERNETES-NATIVE API GATEWAY FOR MICROSERVICES BUILT ON ENVOY」な[AMBASSADOR](https://www.getambassador.io/)も気になるところです。

## 最後に

数年後にはブラウザ側にネイティブ実装され、ブラウザのデベロッパーツールも充実し、各言語に標準ライブラリとして用意されてくるはずです。
自社のサービスをAPIで公開していくのが当たり前の時代となり、RESTインターフェースを採用するのが一般的になりましたが、今後はgRPCのインターフェースの公開も考えていかないといけないと思います。
ただ、gRPCインタフェースを公開するとしても、ここまでデファクトとなったRESTがすぐになくなる事はありえないので当分は両方を運用していく事になりそうです。
その場合は[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)を使って上手く対応できれば開発がスムーズになる気がします。
自社サービス内だけで利用する非公開APIであればgRPC一択に振り切るのもありではないでしょうか。

個人的に今後のgRPCの発展がとても楽しみなので逐一情報を追っていきたいと思います。