blog.syfm

徒然なるままに考えていることなどを書いていくブログ

gRPC-Web Internals

これは Aizu Advent Calendar 2018 の 2 日目の記事です。
1 日目は @Ruto009 さんで、3 日目は @ayanoaizu さんです。

adventar.org

gRPC-Web is GA!

今年の 10 月末に、gRPC-Web がついに GA (Generally Available) となり、メジャーリリースが行われました 🎉

grpc.io

gRPC は、HTTP/2 ベースの RPC フレームワークで、IDL (Interface Definition Language) を元にクライアント・サーバのスタブを各プログラミング言語向けに生成できる、といった特徴を持っています。
gRPC-Web は、gRPC ベースのプロトコルで、gRPC を Web ブラウザ環境から利用するために作られました。

では、Web ブラウザ環境で動作するために、gRPC-Web ではどのような拡張が行われているのでしょうか?
この記事では gRPC と gRPC-Web を比較し、その違いを見ていきたいと思います。

この記事は、今年行われた技術書典 5 で執筆した章「gRPC Web Internals」を元に構成されています。
技術書典からしばらく経ったため、この章のみを一般公開したいと思います。この記事より詳しい解説はそちらの方をぜひご覧ください!

github.com

仕様

gRPC と gRPC-Web の仕様は共に gRPC の GitHub リポジトリ内に存在します。

github.com

gRPC の仕様は以下のディレクトリに、

grpc/PROTOCOL-HTTP2.md at master · grpc/grpc · GitHub

gRPC-Web の仕様は以下のディレクトリにあります。

grpc/PROTOCOL-WEB.md at master · grpc/grpc · GitHub

以下ではこれらの仕様から特に特徴的な箇所を紹介します。

HTTP のプロトコルバージョン

gRPC は、HTTP/2 ベースのプロトコルです。すべての通信は HTTP/2 によって行われ、デフォルトで TLS が有効になっています。ただし、開発時などの都合のために TLS を無効にした非セキュアな通信を行うこともできます。例えば、Go がクライアント・サーバの場合、WithInsecure オプションを使うことで実現可能です。

それに対し、gRPC-Web は状況に応じて HTTP/1 または HTTP/2 のいずれかが使用されます。ほとんど全ての Web ブラウザでは HTTP/2 での通信において TLS が必須となっているため、そういった場合は HTTP/1 による非セキュアな通信にフォールバックされます。

また、HTTP/2 はバイナリベースのプロトコルであるのに対し、HTTP/1 はテキストベースのプロトコルです。gRPC のデフォルトのシリアライザである Protocol Buffers 3 によってシリアライズされたデータを HTTP/1 で扱うためには、その内容をテキストとして扱えなければいけません。そのため、gRPC-Web では Base64 をデフォルトのエンコーダ / デコーダとして規定しています。実際、gRPC-Web による通信をブラウザから見てみると Base64 エンコードされていることが分かります。

f:id:ktr_0731:20181130215714p:plain
Base64 エンコードされたレスポンスボディ

gRPC-Web のデザインゴールの一つとして、「HTTP/1 で代替できない、HTTP/2 特有のフレームや振る舞いを分離する」といったものがあります。
例えば、gRPC の仕様 で使用されている GOAWAY フレームや、PING フレームを HTTP/2 での通信でも使用せずに、HTTP/1 のセマンティクスのみで表現できるようになっています。

RPC 方式

gRPC には、

  • Unary RPC
  • Server streaming RPC
  • Client streaming RPC
  • Bidirectional streaming RPC

の 4 種類の RPC 方式があります。
Unary RPC は一つのリクエストに対して、一つのレスポンスが返り、Server streaming RPC は一つのリクエストに対して、複数のレスポンスが返ります。
Client streaming RPC は Server streaming RPC とは逆に、複数のリクエストに対してただ一つのレスポンスが返ります。
Bidirectional streaming RPC は複数のリクエストに対して複数のレスポンスが返ります。

Unary / Server streaming RPC

これら二つの RPC 方式は gRPC-Web でもサポートされています。レスポンスボディは Base64エンコードされていますが、その中身は通常の gRPC と同様のスキーマになっています。このスキーマABNF で表現されています。
後述する improbable-eng/grpc-web では、トレイラーヘッダ名が lower-case header ではなく MIME header 形式になっているという、gRPC / gRPC-Web の仕様から外れた実装がありましたが、v0.6.4 で修正されています。

Client / Bidirectional streaming RPC

現在、これらの RPC 方式は gRPC-Web の仕様では 定義されていません。ただし、grpc/grpc-web にあるロードマップによると、将来的に WebSocket を使った実装を追加する可能性があるといった記述がありますが、これはディスカッションで提案されているもので、仕様の一部ではありません。

バイナリシリアライザからの切り替え

gRPC では Protocol Buffers や FlatBuffers のようなバイナリベースのシリアライザが使用されていますが、これらはクライアントがスキーマを把握するために巨大なメタデータを必要とします。また、Web ブラウザ上でのデシリアライズも CPU 負荷が大きくなりやすいため、Gmail の特殊な JSON のように軽量で効率的なテキストベースの形式に切り替えていくことを検討しています。

その他の gRPC との違い

他にもいくつかロードマップに挙げられている機能があります。詳しくはリンク先を見てみてください。

gRPC-Web の実装たち

ここまで、gRPC-Web の仕様をかいつまんで見てきました。では次に実際の実装はどのようなものがあるか見てみましょう。
現在、主もアクティブな実装が二つ存在します。一つ目が gRPC オーガニゼーションに属している grpc/grpc-web、もう一方が Improbable 社のエンジニアを中心に開発されている improbable-eng/grpc-web です。

github.com

github.com

これらのクライアント・サーバ実装の互換性を知るには以下のリポジトリが役に立ちます。

github.com

プロキシ

gRPC-Web は gRPC の拡張プロトコルであるため、クライアント・サーバ間でその差異を吸収するためのレイヤーが必要となります。これにはいくつかの方法があります。

grpc/grpc-web

このリポジトリには、JavaScript および TypeScript のクライアントを生成するための Protocol Buffers プラグインおよびプロキシ関連のファイルが含まれています。
プロキシには以下が使用可能です。

improbable-eng/grpc-web

このリポジトリにも grpc/grpc-web と同様に JavaScript / TypeScript クライアント向けのプラグイン、および Go 向けのサーバライブラリなどが含まれています。
プロキシには以下が基本的に使用可能です。

  • Envoy
  • grpcwebproxy

また、improbable-eng/grpc-web では grpcweb という Go パッケージが提供されており、プロキシを使わずに、grpc.Server をラップすることで gRPC ⇔ gRPC-Web の相互変換を行うことができます。

improbable-eng/grpc-web の最も特筆すべき点として、Client / Bidirectional streaming RPC を独自に実装していることが挙げられます。
この実装には、WebSocket が使われています。
そのため、improbable-eng/grpc-web を使うことで、これらの RPC も扱うことができるようになり、ネイティブの gRPC と同等の機能性を得ることができるようになります。
ただし、Envoy や NGINX などのプロキシを使うことができなくなる点に注意してください。
また、前述の通り、WebSocket を使用した実装は仕様にはない拡張であるため、今後 grpc/grpc-web で実装されるとも限りません。

gRPC-Web の gRPC サポート

将来的に、WHATWG Fetch / Streams が多くのモダン Web ブラウザで使用可能になった段階で gRPC-Web はネイティブの gRPC プロトコルをサポートすることが検討されています。
ただし、以下の各 Web ブラウザのサポート状況を見る限り、当分先になりそうな気がします。

developer.mozilla.org

developer.mozilla.org

gRPC-Web がネイティブ gRPC プロトコルとの通信をできるようになれば、当然 Client / Bidirectional streaming も可能になります。
gRPC-Web / gRPC の違いを意識せずに透過的に利用できるようになるにはここまで成熟する必要があるでしょう。

宣伝

対話的にサーバと通信ができる gRPC クライアントの Evans を作っています。

github.com

Evans は gRPC-Web をサポートしているので、通常の gRPC サーバと同様にテストすることができます! 多分汎用 gRPC クライアントで gRPC-Web をサポートしているのは Evans だけだと思います。

ぜひ使ってみてください 🙏

就活を終えた

はじめに

この記事はポエムです。

f:id:ktr_0731:20181029161538j:plain


先日、内定承諾をして就活が終わった。
就活を始めたのが去年の 9 月くらいだったので、だいたい 1 年くらい就活をしていたことになる。
とはいえ、内定承諾をしたのが最近だっただけで実質的な就活は去年の年末で終わっていた。
それからは頂いた内定を 1 つずつ断り、残った 1 つだけ承諾せずに残していた。残していた明確な理由は特になくて、ただ内定承諾期間が長いからというだけだった。

ついに社会人になるのか、と思うと感慨深いものがあるので、今までなにをしてきたか・なにを思ってきたかを鮮やかに思い出せる間に書いておきたいと思う。

高校

高校は工業高校の情報技術を扱う学科だった。第 1 志望は高専で、それに落ちて第 2 志望のこの学校に行くことになった。高専では電気科志望だったのでそちらに行かなくてよかったと今では思っている。
高等教育機関ではないので当たり前だけど、高校ではコンピュータサイエンスというより、就職するために必要な知識を学ぶような感じだった。C 言語の文法を 3 年間かけて学び、ハードウェア・ソフトウェアともに浅く狭く学んだ。
一応プログラミング歴はその頃から始まっていたことになるけど、当時は Linuxコマンドラインといった環境も全く使っていなかったし、知らなかった。

大学

大学は会津大学という福島の公立大学に通っている。大学に行くということは、それだけで実家に負担がかかるので、公立かつ県内出身の人は入学費が半額になる会津大学を選んだ。工業高校から推薦を使わずに大学に行くということは、めちゃくちゃ難しくて、うちの高校ではセンター試験は受けない前提のカリキュラムなので当然普通科の高校生より圧倒的に学力が劣っていたと思う。会津大学の偏差値は高くないけど、自分も受験期は地獄を見た。

この大学は、コンピュータ理工学部しかないことで有名で、大学の規模は大きくないけど通っているすべての学生がコンピュータサイエンスを学んでいる。残念なことに、会津大学国公立大学の滑り止めに使われることが多く、落ちてきた人がかなりの割合を占めている。結構そういう人はコンピュータサイエンスを学ぼうという意識も低いように感じるし、そういった人に影響を受けやすい環境であることは間違いない。
ただ、毎年一定数異常にコンピュータに精通している人たちが入学しているので、そういった人たちと関わっていると良い刺激を受けることができると思う。

授業の質は普通という感じで、特に良いと思ったのはオートマトンと言語理論、言語処理系論、離散系論くらいしかなかった気がするけど、UNIX 環境があり、広く浅くコンピュータサイエンスを学べる大学は日本にはあまりなさそう。

ベンチャーアルバイト

会津大学発のベンチャーが市内には多くある。ただし、実際にはベンチャーではなくただの零細企業であるところがほとんどというのが現状な気がする。
それでも比較的まともな企業には大学の先輩が多く在籍していて、そういったところでアルバイトとして働くことで色んな技術的なことを教えてもらえると思う。
自分も学部 1 年から 4 年の春頃まで計 2 社の企業で働いていた。一社目は JavaScript (Node.js 含む) とか PHP とかを使う業務をいろいろやっていて、二社目ではバックエンドエンジニアとして Go で Web API サーバを書いていた。

ただ、会津大学のある会津若松市は他にコンピュータサイエンスの大学もなく、遊ぶ場所もなく、山に囲まれた良くも悪くも閉鎖的な環境なので気をつける必要がある。 大学に入ったばかりのときは、意識が高いのでどんな企業も魅力的に見えるけど、実際にはひどい企業もたくさんある。なにかおかしいなと気がついたらその会社はやめるべきだと思う。世の中にはたくさんの企業があり、わざわざ怪しい企業で働く必要はない。自分も辞めた。

今は学生ニートをしつつ、やりたいことをやってるような感じ。会津若松は、なにもないけど自然豊かで静かなので余生を過ごしている感が得られて便利。

インターン

インターンにもいくつか行った。ここで書いていない企業もいくつかある。

リクルート

2 年の時にインターンに応募した。実際には技術面接で落とされた。このときの評価は未だに納得がいっていなくて根に持っている。

はてな

3 年の夏。はてなは昔一番好きだった Web サービスを作っていた会社で、ここでインターンとして働けて本当に良かったと思った。
Scala や Angular を使って Mackerel の機能開発をがんばっていた。
詳しいことは別の記事で書いているのでそちらを。

syfm.hatenablog.com

Wantedly

3 年の夏、はてなインターン終わりの翌週から。2 年の冬頃に Wantedly に遊びに行ったことがあって、気になっていたのでインターンに参加した。
Go のバックエンドを希望していたのに、なぜか未経験の Rails を使っているプロダクトに入ることになった。
採用面接の技術試験は一番難しかった気がする。

Fringe81

3 年の夏、Wantedly の次に行った。ここは年齢層が割と高く他の Web 系の企業とは雰囲気が少し異なる。
ここでは DDD の戦術的設計を行うインターンをして、かなり悩んで苦しかったけどすごくいい経験になったのを覚えている。

就活要素

就活をするにあたって、ある程度内部が分かる企業を選ぼうと思っていた。 あとは、インターン先からも分かるように、Web 系企業を前提に考えていた。

大学

特に Web 系の企業に行きたい人の場合、大学の就職課は完全に当てにしないほうが良い。学部 3 年になると進路についての面談をさせられるけど、自分の挙げた企業名がほぼわからなかったらしく、適当な雑談をして終わった。
最近だと大学経由で一部のインターンを応募できるようになったらしいけど、レポートなりが必要で面倒なので自分で直接応募したほうが間違いなく良い。

交通

面接を受ける最低限の基準として、交通費を全額支給してくれるか否かというのがある。 自分みたいな地方の学生だと交通が悪かったり遠かったりで、東京への参勤交代は肉体的にも精神的にもかなり消耗することになる。 自分の場合だと、東京 ⇔ 会津若松 で、新幹線を使う場合は 3 時間、高速バスを使う場合は 5 時間ほどかかる。 一番多い時期だと週に 1 度は東京へ行っていたので、交通費支給 + 新幹線じゃないと金銭・肉体・精神がマッハで消耗されていく。
自分が面接を受けた企業からは、どこも交通費を支給していただいたので本当にありがたかった。

逆求人

逆求人にも一度行った。アカウントつくって、それなりにプロフィールを充実させておくと早い段階で逆求人の招待が来る。逆求人の開催時期が早いほど企業の質も良い気がしていて、自分が行ったときも興味のある企業ばかりだった。
ただ、異常に疲れるし二回目は一回目と被る企業も出てくるので一度だけ行けば十分だと思う。

資格

一応、就活が始まる前までに情報処理技術者試験の FE / AP / SC / DB を持っていたけど全く役に立たなかった。
そもそも、ソフトウェアエンジニアならこの程度の試験問題なら絶対に解けなければいけないと思って勉強していたので、自分もこれらを武器にしようと思っていなかったし、話題に出すこともなかった。

基準

自分の選んだ企業を選んだ理由はいくつかある。
一緒に働きたい人がいる、使っている技術・興味のある技術がある程度一致している、事業領域に興味があるといった点が大きいかなと感じる。
それぞれのインターンでメンターとなったエンジニアはどの方も本当に尊敬できて、そのたびに一緒に働きたいと思った。 メンターだけではなく、インターンの同期やアルバイト先の学生ともそう思ったときが度々あった。 正直かなり悩んだのも事実だけど、自分も他のエンジニアもずっと同じ企業に属するわけでもないのでいつか一緒に働けるときが来れば良いなぁと思っている。

まとめ

振り返ってみると、それなりに就活っぽいことをやっていたことに気づいた。

企業の性格を見たいならその企業で働いてみるのが一番良いと思う。一週間だと微妙だけど二週間くらいいると大体わかってくる。
自分は、自分の実力に自信が持てない性格なので、企業で働いているエンジニアは全知全能の神のような存在に思える。ただ、一緒に働いていると実は自分のほうが得意、知っている知識があったりして、こんなにすごいエンジニアでも人間なのかー。と実感することができると同時に頑張ろうと思えるようになってくる。

インターンに行くか否かっていうのは人の好き嫌いがあるのでどちらとも言えなさそう。研究や CS の勉強、OSS 活動のほうが好きであればそういうところに割いたほうが良いかもしれない。どちらにせよ、何かに打ち込んでいてそれを伝えられれば、ちゃんとした企業であれば評価してくれるはず。

あとは特に大きなイベントもなく、淡々と春になるまで雪に埋もれているだけなので、卒業できるように卒論がんばりながら、学生の間にやりたいことをやっておきたいなと思う。

技術書典 5 で Go & Kotlin 本を頒布します

技術書典 5 の「う11」で「Go & Kotlin Playground」というタイトルの本を頒布します。 以下のような本です。表紙は入稿 1 時間前につくりました。

f:id:ktr_0731:20181003230047j:plainf:id:ktr_0731:20181003230056p:plain
Go & Kotlin Playground

ホームページもつくりました。 gcf.syfm.me

割とニッチな章ばかりかなぁと思います。
自分は第 1 章「gRPC Web Internals」と第 2 章「やはり俺の Go アプリケーション設計はまちがっている。」を担当しました。

第 1 章で取り上げている gRPC Web は gRPC を Web ブラウザ上で使うための gRPC ベースのプロトコルです。
gRPC と比べて具体的に何が異なっているのか?実用的なものなのか?将来的な目的はなんなのか?そういったことに触れています。
また、gRPC Web プロトコルの実装である improbable-eng/grpc-web での通信を読み解いて、どういうリクエスト・レスポンスが行われているのかを覗いてみたりもします。

github.com

gRPC Web を触ったことのない人も興味があればぜひ手に取ってみてください。

第 2 章「やはり俺の Go アプリケーション設計はまちがっている。」は Go アプリケーションの設計に関する章です。 Go を書く時に、パッケージをつくるべきか?一つのパッケージにまとめるべきか?どういうパッケージ構成にするか?といったことに悩んだ方も多いかと思います。
この章では、Go アプリケーションを開発するときに、どういう方針で構築していけばいいかを Go の標準パッケージの特徴や、Go 自体の設計思想などから考えていきます。
この章は、先日行われた Go の学生コミュニティによるイベント、Student Go #1 で発表した内容です。

speakerdeck.com

ここでの内容をベースに、その時の発表で話せなかったことや、補足、文章の再構成を行っています。
「Go アプリケーション設計」と銘打っていますが、実際にはすべてのソフトウェア開発共通で言える設計原則なので、Go をあまり書いたことのない人でも設計に興味がある方はぜひ手に取っていただければと思います。

このサークルの出展は今回が初めてとなります。色々至らない点があるかもしれませんが、少しでも興味があったら、ぜひ立ち寄ってください!

techbookfest.org