◻️

gRPC-WEBについて調べたのでまとめる

2024/08/21に公開

TL;DR

  • なんか色々複雑な話だった

gRPCとは

  • gRPCのRPCは Remote Procedure Call だが、gの部分はGodとかGoogleとか諸説あるらしい🥺
  • Protocol Buffers を使ってデータを変換して通信する(変換後にzip処理もできる)
  • IDL(インターフェース定義言語)(.protoファイル)があるため、スキーマファーストで開発できる
  • HTTP/2を使用して作られている(HTTP/3を使うバージョンも検討中っぽい?)
  • HTTP Trailer(後述する)を多用している
  • HTTP/2経由のバイナリ通信と同じでは?と思うかもしれないが、より高度な機能を提供しているらしい

話は逸れるが HTTP Trailer について

Transfer-Encoding: chunkedのときのみ利用可能で、Body送信後にしか分からないチェックサムなどをリクエストに付与したいときなどに使うもの。

gRPC は HTTP Trailer を多用するが、Web ブラウザーを含む 多くの HTTP 実装は、まだトレーラーをサポートしていない。

gRPC-WEB では、応答本文の末尾にトレーラーをエンコードして付与することでその問題を解決しているとのこと。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Trailer

ちなみに、2024-09現在、Nginxではbodyにトレーラーを付ける実装が無いため、不完全なgRPC-WEB実装となっていて後述するconnect-rpcで使えなかったりする。

https://github.com/connectrpc/connect-es/issues/1115

gRPC-WEBとは

ブラウザが対応しなくても、gRPCを使えるようにしたもの。

  • HTTP1.1でも動作するように、変換処理を挟んで通信する(envoy, nginxなど)
    • 変換処理込みのgRPCサーバがあれば、envoyなどは不要となる(Rustのtonic-webで確認)
  • gRPCの Unary RPCsServer-side Streaming RPCsのみサポート
  • クライアントストリーミング RPC と双方向ストリーミング RPC は未対応(HTTP/2のみの機能なため)
  • POSTリクエストで、Protocol Buffersのバイナリを送信するスタイル
  • content-typeは、application/grpc-web or application/grpc-web+proto

curlでgRPC-WEBをコールする場合は下記のような感じとなる

## (--data-rawだとうまくいかないので、--data-binaryに標準入力からバイナリを渡している)
printf '\x00\x00\x00\x00\x05\n\x03aaa' \
     | curl -v 'http://[::1]:50051/helloworld.Greeter/SayHello' \
      -X POST \
      -H 'content-type: application/grpc-web+proto' \
      --data-binary '@-' \
      --output -

結果は下記のような感じ

> POST /helloworld.Greeter/SayHello HTTP/1.1
> Host: [::1]:50051
> User-Agent: curl/8.7.1
> Accept: */*
> content-type: application/grpc-web+proto
> Content-Length: 10
>
* upload completely sent off: 10 bytes
< HTTP/1.1 200 OK
< content-type: application/grpc-web+proto
< access-control-allow-origin: *
< vary: origin
< vary: access-control-request-method
< vary: access-control-request-headers
< transfer-encoding: chunked
< date: Wed, 21 Aug 2024 04:41:04 GMT

HTTP Trailer を使う場合、transfer-encoding: chunked である必要があるらしい。

gRPC-Gatewayとは

gRPC-WEBと似ているが、変換部分がJSONとなっており、より簡単に通信できる。

だがしかし、下記の欠点がある。

  • proto ファイルが変更された場合、gRPC-Gatewayサーバのコードも再生成する必要がある(リリースが複雑になる)
  • protobufからjsonに変換しているのでオーバーヘッドがある(gRPC-WEBは、protobufをそのまま使っている)
  • proto ファイルからフロントエンドのコードが生成されるわけではないので、管理が煩雑?(コード生成までやるライブラリがあれば別だがそこまで調べてない)

Connect-rpcとは

Connectは、サーバとクライアントが用意されており、envoyなどのミドルウェア無しで下記全種類の通信ができる。

  • gRPC
  • gRPC-WEB
  • gRPC-Gateway

注)ブラウザからの場合、gRPCでは通信できない

サーバとクライアントで独立したモジュールなので、クライアントだけConnectを使うなどできる。

余談だが、ConnectのWebクライアントはデフォルト gRPC-Gateway 推しっぽいので、Quic Startの記述をそのまま使うと gRPC-WEB では通信できない。

変更したい場合createConnectTransportcreateGrpcWebTransport に変えれば良い。
https://connectrpc.com/docs/web/choosing-a-protocol#grpc-web

Envoy不要のIn-process Proxy

In-process Proxy とは、各言語のgRPCサーバ内でgRPC-WEBも扱えるように変換する処理のこと。

Envoyのなどのミドルウェアによる変換層を挟まなくて済むため、シンプルかつ分かりやすくなる。

もし、Envoyが無いとgRPC-WEBが使えないと思っていた場合、自分の使用している言語に変換用のライブラリがないか?確認してみると良い。

ContentTypeについて

application/grpc-web

例: application/grpc-web+[proto, json, thrift]

送信者は常にメッセージ形式を指定する必要があります(例:+proto、+json)
受信側は、Content-Type にメッセージ形式が指定されていない場合 (「application/grpc-web」など)、デフォルトが「+proto」であると想定する必要があります。

application/grpc-web-text

「application/grpc-web」のテキストエンコード(base64)されたストリーム

例: application/grpc-web-text+[proto, thrift]

こちらも指定なしだとデフォルトはprotoで、protoをbase64したものになる。

余談(Thrift)

ThriftはApache Thriftのことで、元々 Facebook(Meta) で開発されたもの。
gRPCと似た機能を提供している(IDLもある)

Apache Thrift は、言語に依存しない方法でインターフェースを定義し、データをシリアル化することで、分散システムにおける言語とプラットフォームの異種性の問題を解決します。
これにより、チームは好みのプログラミング言語で作業しながら、マイクロサービスとクライアント間の効率的な通信が可能になり、スケーラブルなクロスプラットフォーム アプリケーションの構築に最適な選択肢となります。

ブラウザではgRPCのネイティブ通信ができないし今後もできるようにはならなそう(後述するwhatwg/streamsで出来そう)

ブラウザ方針として、JavaScriptからのHTTP通信は1.1と2を意識させずに透過的に扱う方針。
そのため、HTTP/2に固定する機能は提供したくないっぽい。

whatwg/streamsがブラウザに実装されればgRPC-WEBはオプションになる

2024現在から2年以内に、whatwg/streams が完成して、ブラウザに実装される予定らしい。

そちらが使えるようになれば、ブラウザ側からgRPCをネイティブかつ、全パターンの通信が扱えるようになるらしい。

最後に

whatwg/streams があってgRPCのネイティブ通信ができるようになったら、この記事も不要になるなぁと思った次第。。。

参考記事

[gRPC] Connectについて 作られた背景(概要+α) と チュートリアル+αを通して基本的な機能を押さえる
https://qiita.com/SYM_simu/items/85d572e3520e98e09044

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

GRPC PHP 1.66.0
https://grpc.github.io/grpc/php/md_doc__p_r_o_t_o_c_o_l-_w_e_b.html

grpc-web streaming-roadmap.md
https://github.com/grpc/grpc-web/blob/master/doc/streaming-roadmap.md

GitHub whatwg/streams
https://streams.spec.whatwg.org/#intro

Discussion