Webアプリケーション通信において、gRPCはRESTやWebSocketsに取って代われるか?
急速に進化するWeb開発の状況において、効率性とパフォーマンスは新しい技術の採用において常に最優先事項となります。 gRPC-Webライブラリで行われている作業は、開発者が gRPC の速度とパワーをWebアプリケーションのクライアントサーバー通信に活用する方法において、RESTおよびWebSocketのいくつかの側面を置き換える画期的な変化をもたらします。従来のRESTful呼び出しやWebSocket接続との比較分析を行い、各アプローチでの比較のためにgRPC-Webを使用した実践的なコードサンプルを提供しましょう。
gRPC-Webと現代のWeb開発におけるその位置づけの理解
gRPC-Webの誕生は、より応答性の高い、低遅延のWebアプリケーションへの探求に端を発します。これは、高性能でオープンソースのユニバーサルRPCフレームワークであるgRPCの機能をブラウザに拡張し、通常はサーバー間通信に指定されるgRPCサービスとの直接通信を可能にします。gRPCは、 Protocol Buffers (Protobuf) と呼ばれるシリアライゼーション形式を中心に構成されており、ペイロードを小さくし、開発プロセスを合理化する明確なインターフェース記述を促進します。
技術的な詳細に入る前に、gRPC-Webが表す可能性のあるシフトを検証しましょう。ネイティブgRPCプロトコルはHTTP/2を必要としますが、gRPC-Webはその要件を緩和し、ブラウザ環境で利用可能な任意のHTTP/*プロトコルをサポートできるようにします。WebSocketシナリオでは、永続的な接続が全二重通信のために維持されますが、WebSocketはさまざまな接続状態の管理に複雑さをもたらす可能性があります。gRPC-Webは、サーバーストリーミング機能により、より効率的なリアルタイムデータフローをもたらす魅力的な代替手段を提供します。現時点では、ブラウザの制約により、gRPC-Webはクライアントサイドストリーミングをサポートしていません。
gRPC-Webの仕組み:その動作方法
gRPC-WebをWebアプリケーションに統合するには、特定のアーキテクチャを採用する必要があります。このアーキテクチャの中心となるのは Envoyプロキシ であり、WebアプリケーションとgRPCサーバー間のブリッジとして機能します(これにより、ネットワークプロトコルの抽象化が可能になります)。EnvoyはgRPC-Web呼び出しをgRPC呼び出しに変換し、HTTP/1.1からHTTP/2への変換を処理して、ブラウザがgRPCのメリットを享受できるようにします。
この通信モデルに関わるステップを分解してみましょう
- ブラウザがgRPC-Webクライアント呼び出しを開始します。
- Envoyプロキシが、Protobufで定義されたリクエストを含む呼び出しを受信します。
- EnvoyはこれをHTTP/2 gRPC呼び出しに変換し、gRPCサーバーに転送します。
- gRPCサーバーはリクエストを処理し、Envoyに応答を返します。
- EnvoyはgRPC応答をgRPC-Web形式に変換し、クライアントに送信します。
このプロキシシステムを使用することで、gRPC-Webは、パフォーマンスが高く、Protobufの強力なデータ型のおかげでデータの明瞭性と精度を提供する、堅牢なクライアントサーバーインタラクションを促進します。
RESTからgRPC-Webへの移行の理論
RESTに慣れている開発者にとって、gRPC-Webへの移行は困難に思えるかもしれません。関係するコンポーネントと段階的なアプローチを適切に理解すれば、移行はスムーズに行えます。
典型的なRESTful fetch呼び出しを考えてみましょう
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Accept': 'application/json',
},
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
上記のコードは、RESTful APIサービスからJSONデータを取得します。fetch API、HTTPメソッド、およびコンテンツタイプヘッダーの使用に注目してください。応答はJSONオブジェクトとして処理され、エラー処理はPromiseチェーンに組み込まれています。このコードに示されていないのは、ペイロードで受信したデータが期待されるスキーマと一致することを保証するために必要なデータ検証、およびスキーマで受信したデータが要求されたデータ型に正しく設定されていること、そしてエラーのあるデータを処理するユーザーエクスペリエンスです。
gRPC-Webでこれを再考してみましょう
const { ExampleRequest, ExampleResponse } = require('./generated/example_pb.js');
const { ExampleServiceClient } = require('./generated/example_grpc_web_pb.js');
const client = new ExampleServiceClient('https://api.example.com');
const request = new ExampleRequest();
client.getExampleData(request, {}, (err, response) => {
if (err) {
console.error('Error:', err);
} else {
console.log(response.toObject());
}
});
このgRPC-Webの例では、まず必要なProtobuf定義とクライアントスタブをインポートすることから始めます。サービスURLを指定してクライアントインスタンスを作成します。リクエストオブジェクトを構築し、クライアントで `getExampleData` メソッドを呼び出し、リクエストと応答またはエラーを処理するためのコールバック関数を渡します。
アプローチの明らかな違いに注目してください:gRPC-Web呼び出しは強く型付けされており、シリアライゼーション/デシリアライゼーションは開発者ではなくライブラリによって処理されます。この型安全性と自動化により、人的ミスの可能性が大幅に減り、開発プロセスが合理化されます。オブジェクトを受け取った場合、それはすでに完全に検証されています。
RESTに対するgRPC-Webの利点
RESTは何年もの間Web APIの基盤となってきましたが、その単純さは複雑なWebアプリケーションに関しては限界となることがあります。gRPC-Webは ブラウザでサポートされている任意のHTTP/*プロトコル で動作しますが、gRPC-WebはHTTP/2の多くの機能を利用し、多くの改善をもたらします。HTTP/2とgRPC-Webの利点は以下のとおりです。
- 既存のサービスで動作します: Envoyプロキシ以外に新しいものを構築する必要はありません。したがって、gRPC-Webを実装すると、既存のgRPCサービスにアクセスできます。これは、JavaScriptライブラリを利用するモバイルアプリを含むアプリケーションにとって利点となります。
- 型安全性: gRPC-Webでは、リクエストと応答はProtobuf定義に基づいて強く型付けされます。クライアントとサーバー間のこの契約は明示的であり、誤解やバグの可能性を減らします。
- 効率的なシリアライゼーション: gRPCで使用されるシリアライゼーション形式であるProtobufは、JSONまたはXMLよりも効率的であり、シリアライゼーションが速く、メッセージサイズが小さくなります。これはパフォーマンスにとって特に有益であり、帯域幅の観点からコスト削減につながる可能性があります。HTTP/1.1では、データをテキストモードまたはバイナリモードで送信できますが、両方で送信することはできません。HTTP/2はバイナリのみであり、バイナリをテキストにエンコード/デコードすることは、混合ペイロードをREST経由で送信するためにバイナリファイルをテキストにエンコード/デコードすることよりもエラーが発生しにくいです。
- 明確なAPI契約: サービス定義にProtobufを使用すると、明確で言語に依存しないAPI契約が作成されます。これは、複数の言語でクライアントおよびサーバーコードを生成するために使用でき、開発者にとってシームレスなエクスペリエンスを提供します。
gRPC-Web環境のセットアップ
gRPC-Webを開始するには、Protobufを使用してサービスとメッセージペイロードを定義し、gRPCバックエンドサービス(または 一時的にはモックサーバー)をセットアップし、gRPC-WebとgRPC間の変換を処理するEnvoyプロキシを構成する必要があります。
まず、.protoファイルでサービスを定義します
syntax = "proto3";
package example;
service ExampleService {
rpc GetExampleData(ExampleRequest) returns (ExampleResponse);
}
message ExampleRequest {
string query = 1;
}
message ExampleResponse {
repeated string data = 1;
}
この ` .proto ` ファイルは、リクエストとレスポンスのメッセージ形式とともに、単一のRPCメソッド ` GetExampleData ` を持つ単純なサービスを定義します。操作はリクエストで単一の ` ExampleRequest ` メッセージを送信し、レスポンスで単一の ` ExampleResponse ` メッセージを受信することを期待するため、この単項RPC呼び出しはRESTfulリクエストを模倣します。
次に、適切なgRPC-Webプラグインを使用してprotocコマンドラインツールでサービス用のクライアントスタブコードを生成します。(gRPC-Webクイックスタートドキュメントの例)。このプロセスにより、ブラウザからgRPC-Web呼び出しを行うために必要なJavaScriptクライアントファイルが作成されます。
選択した言語でgRPCサーバーが実装されたら、Envoyプロキシを構成します。gRPC-Webクイックスタートドキュメントの別の例を次に示します。。
上記でリンクされたより大きな構成の一部としてgRPC-Webを有効にするEnvoy構成のYAML構文を次に示します。
http_filters:
- name: envoy.filters.http.grpc_web
- name: envoy.filters.http.router
これらの要素が配置されたら、WebアプリケーションからgRPC-Web呼び出しを開始できます。
Protobufによるサービスメソッドの定義
サービスメソッドを定義する際、Protobufはリクエストとレスポンスのメッセージ構造を定義することにより、単一の真実の源として機能します。この厳密なスキーマにより、複数の言語でのクライアントおよびサーバーコードの自動生成が可能になります。特にJavaScriptの場合、このコード生成により、ブラウザクライアントの呼び出しプロセスが合理化されます。
上記の例の .proto ファイルを使用すると、生成されたJavaScriptクライアントコードはこれらの定義を使用して、正しいデータ型のみが送信および受信されることを保証します。このプロセスは、RESTfulサービスでエラーが発生しやすい手動のデータ検証と解析の多くを処理します。
典型的なWebSocket接続をgRPC-Webで置き換える
WebSocketは、単一の長期間接続された接続 over 全二重通信チャネルを提供します。gRPC-Webが クライアントストリーミング機能の欠如 によりWebSocketを完全に置き換えることができないシナリオでは、効率的なサーバーからクライアントへのストリーミングに使用できます。
典型的なWebSocket実装例を次に示します
const socket = new WebSocket('ws://example.com/data');
socket.onmessage = function(event) {
const receivedData = JSON.parse(event.data);
console.log(receivedData);
};
socket.onerror = function(error) {
console.error('WebSocket Error:', error);
};
WebSocket APIは単純ですが、接続の状態とライフサイクルを管理することは複雑になる可能性があります。
さて、サーバーサイドストリーミングがgRPC-Webでどのように見えるかを見てみましょう
const { Empty } = require('./generated/common_pb.js');
const { DataServiceClient } = require('./generated/data_grpc_web_pb.js');
const client = new DataServiceClient('https://api.example.com');
const request = new Empty();
const stream = client.dataStream(request, {});
stream.on('data', (response) => {
console.log(response.toObject());
});
stream.on('error', (err) => {
console.error('Stream Error:', err);
});
stream.on('end', () => {
console.log('Stream ended.');
});
WebSocketをgRPC-Webで置き換えるにはより多くのコードが必要ですが、サーバーがクライアントに継続的にメッセージを送信できるサーバーサイドストリーミング呼び出しを設定できます。クライアントはイベントリスナーを使用して、受信メッセージ、エラー、およびストリームの終了を処理します。これはWebSocketとは異なるパラダイムですが、サポートされているユースケースのコンテキストでは、より効率的で管理しやすいものになる可能性があります。
WebSocket経由の多くのチャットアプリケーションは、単一のクライアント送信とサーバーからのストリームイベントを利用しており、これはgRPC-Webで置き換えることができます。マルチプレイヤーゲームが開発されたシナリオであっても、「MoveCharacters」のようなRPC呼び出しは、キャラクターを移動させるブラウザからの単一メッセージを受け取り、他のプレイヤーやコンピューター制御のキャラクターのすべての移動をストリームバックできます。
RESTとWebSocketを置き換える時期か?
この記事では、gRPC-Webを使用してRESTとWebSocketを置き換えることの表面をかすめ、その理由と実践的なコードサンプルで開始する方法に焦点を当てました。エラー処理を完全に組み込むには、さらに作業が必要であり、パフォーマンスベンチマークを示すことも、このドキュメントの範囲外です。
Envoyを使用したgRPCとgRPC-Webの多くの技術的側面は、最新のWebアプリケーション開発においてRESTとWebSocketに取って代わることができます。 公開されているgRPC APIはほとんどありません が、より多くの企業がHTTP/2およびHTTP/3ベースのAPIのパフォーマンスを活用し、Webアプリケーションの代替の新興技術を検討するようになるのは時間の問題です。
PostmanにおけるgRPCサポート
APIを扱っているなら、おそらくPostmanを使用しているでしょう。 PostmanがgRPCをサポートしている ことを知っていましたか? VS Code拡張機能もgRPCリクエストをサポート しています。今年のgRPC Confに参加し、コミュニティの皆さんとお会いできて嬉しかったです。ブログで gRPCに関する今後のアップデートや記事 をチェックしてください。Postman内でのgRPC、Protobuf、およびそれらの使用方法の簡単な歴史を知りたい場合は、 Postman Academyコース も参照できます。
技術レビュー:Kevin Swiber (Postman)、Eryu Xia (Google)