Webhooks・WebSockets・gRPCの比較
執筆者 Uttam Singh

ブロックチェーンデータを読み取るアプリケーションは、すべて同じアーキテクチャ上の問いに直面する。データをどうやってシステムに届けるかだ。数秒おきにAPIをポーリングすれば、計算資源を無駄にし、イベントを取りこぼし、レイテンシーが増える。代わりにデータをプッシュするなら、配信モデルを選ぶ必要がある。
リアルタイムデータ配信は主に3つのプロトコルが担っている。webhooks、WebSockets、gRPCだ。これらは互換性のあるものではない。webhookはバックエンドに何かが起きたことを通知する。WebSocketsはブラウザにライブデータをストリーミングする。gRPCはバックエンドサービス間で高スループットのデータ転送を行う。選択を誤ると、その代償は後になって現れる。イベントの取りこぼし、計算資源の浪費、予定になかったシステムの再設計だ。
webhooks、WebSockets、gRPCとは何か
3つとも、ポーリングなしにサーバーからアプリケーションへデータを移動させる。共通点はそこまでだ。
webhookは通常のAPIの向きを逆転させる。コード側がサーバーを呼び出すのではなく、サーバーが関心のある出来事が発生するたびに、登録済みのURLをJSONペイロード付きで呼び出す。webhookの配線は単純だが、その代わりに届く内容やタイミングを自分では制御できない。
WebSocketはクライアントとサーバー間の永続的なTCP接続だ。どちらの側も、接続を再確立することなく、いつでも小さなバイナリフレームを送信できる。プロトコルはHTTPリクエストとして始まり、両者がアップグレードに合意した時点で生のソケットに切り替わる。
gRPCはリモートプロシージャコール(RPC)フレームワークであり、あるサービスが別のサービスの関数を、あたかもローカル関数を呼び出すかのように呼び出せるようにする仕組みだ。GoogleがStubby(社内RPCシステム)の公開後継として開発したgRPCは、HTTP/2上で動作し、データをProtocol Buffersでシリアライズし、完全な双方向を含む4種類のストリーミングモードをサポートする。構造化データをマシン間で高速に移動させるバックエンドサービス向けに構築されており、リトライ、デッドライン伝播、型付き契約が組み込まれている。
webhooksはどのように機能するか
webhookはAPIモデルを逆転させる。サーバーを呼び出すのではなく、サーバーがあなたを呼び出す。プロバイダーにURLを登録し、どのイベントが重要かを伝え、あとは待つだけだ。何かが起きると、プロバイダーはエンドポイントにJSONをPOSTし、2xxのレスポンスを期待する。
app.post('/webhook', (req, res) => {
const sig = req.headers['x-alchemy-signature'];
if (!verify(sig, req.rawBody, SECRET)) return res.status(401).end();
queue.enqueue(req.body);
res.status(200).end();
});上記のスニペットはwebhookハンドラーで、署名を検証し、ペイロードをキューに入れ、200を返す。この単純な形が、本番環境のwebhook障害の大半の元凶でもある。単純なハンドラーと本番用のハンドラーを分けるのは、単純な版が誤りやすい3つの点、署名検証、リトライ処理、バースト過負荷への対応だ。
1つ目の失敗パターンは、署名検証の甘さだ。プロバイダーは共有シークレットを使ってすべての配信にHMAC署名を付ける(AlchemyはX-Alchemy-Signatureを使い、StripeはStripe-Signatureを使う)。ハンドラー側はHMACを再計算し、タイミング攻撃を避けるために定数時間で比較し、タイムスタンプが5分より古いものは拒否する必要がある。verify()を呼び出すのは1行だが、正しく実装するには複数のステップがかかる。どれか一つでも省くと、エンドポイントのURLを知っている任意のプロセスを信頼することになる。
2つ目の失敗パターンは、リトライ時の非冪等な処理だ。配信が失敗すると(タイムアウト、部分的な障害、5xxレスポンス)、プロバイダーは指数バックオフ(通常1秒から倍々に増え、ジッターを加え、上限1時間)で数時間から数日にわたってリトライし、最終的にデッドレターキューに送る。webhookが保証するのは少なくとも1回の配信であって、正確に1回ではない(分散システムにおいて正確に1回の配信は不可能)。したがって重複配信は日常的に発生する。ハンドラーは冪等でなければならない。同じイベントを2回処理しても、1回処理した場合と同じ結果になる必要がある。イベントIDを重複排除ストアで追跡し、繰り返しには200を返せばよい。
3つ目の失敗パターンはバースト過負荷であり、これが大規模なwebhookシステムを破綻させるものだ。単一のオンチェーンイベントが数百万件の同時配信を引き起こすことがある。エンドポイントがボトルネックになれば、プロバイダーのリトライキューがそのままDoS攻撃者と化す。
ブロックチェーンインフラでは、webhookがイベント駆動型ワークフローを支えている。当社のCustom Webhooksは、30以上のEVMチェーンとSolanaにわたって、アドレスアクティビティの監視、NFT転送アラート、GraphQLでフィルタしたスマートコントラクトイベントをサポートし、すべてHTTP POSTコールバックとしてエンドポイントに配信する。
WebSocketsはどのように機能するか
WebSocketは、Upgrade: websocketヘッダーを含む通常のHTTPリクエストとして始まる。サーバーはHTTP 101(Switching Protocols)で応答し、その時点から接続はHTTPを話さなくなる。この折衝がWebSocketハンドシェイクだ。その後に残るのは、メッセージごとのヘッダーもリクエスト-レスポンスのサイクルもない、薄いフレーミング層を上に持つ生のTCPソケットだ。
HTTPを取り除くことで、WebSocketsはメッセージあたりのコストが劇的に下がる。各WebSocketメッセージのオーバーヘッドは2〜6バイト(FINビット、オペコード、ペイロード長)だ。これに対しHTTPでは、リクエストごとに数百バイトのヘッダーが付く。毎秒100メッセージの場合、WebSocketのオーバーヘッドは毎秒約600バイトなのに対し、同等のHTTPトラフィックでは毎秒60,000バイトになる。
const ws = new WebSocket('wss://eth-mainnet.g.alchemy.com/v2/KEY');
ws.send(JSON.stringify({
jsonrpc: '2.0', id: 1, method: 'eth_subscribe',
params: ['newHeads']
}));
ws.on('message', (data) => handleBlock(JSON.parse(data)));WebSocket接続はステートフルで長寿命だ。どちらの側も、相手を待たずにいつでもデータを送信できる。サーバーは定期的にpingフレームを送り、クライアントはpongフレームで応答して接続が生きていることを示す。このハートビートがなければ、切断済みの接続("ゾンビ")がサーバーリソースを消費し続け、リバースプロキシは30〜120秒でアイドル接続を切断する。
トレードオフとして、接続のライフサイクルを自分で管理する必要があり、そのライフサイクルは見た目以上に複雑だ。接続が切れると、クライアントは再接続しなければならないが、指数バックオフ(リトライ間隔を段階的に長くする)を伴わなければ、数千のクライアントが一斉に再接続してサーバーを圧倒してしまう。ロードバランサーの背後に複数のWebSocketサーバーを配置する場合、あるクライアントの再接続は必ず同じマシンにルーティングされなければならない。そのマシンがクライアントのサブスクリプション状態を保持しているからだ。このルーティングルールはセッションアフィニティと呼ばれる。さらに、あるサーバーで発生したメッセージが別のサーバーに接続しているクライアントに届く必要がある場合、サーバー間で状態を共有する必要がある。WebSocketsが提供するのはワイヤープロトコルであり、その上に構築するものはすべて自分で作らなければならない。
ブロックチェーンアプリケーションにおいて、WebSocketsはライブイベントのサブスクリプションにおける標準インターフェースだ。Ethereumのeth_subscribeエンドポイントは、新しいブロックヘッダー、ログイベント、保留中のトランザクションを、永続的なWebSocket接続を通じてプッシュする。当社のSmart WebSocketsは、基本プロトコルの上にフィルタ済みサブスクリプションと自動再接続を追加する。
ブラウザへのライブデータストリーミングは、WebSocketsが解決するために作られた問題だ。WebSockets以前の代替手段(ロングポーリング、サーバー送信イベント)は、メッセージごとにHTTPの全オーバーヘッドを払うか、一方向にしか動作しなかった。WebSocketsは、生のTCPソケットが通常存在できない場所、つまりブラウザにおいて、永続的で低オーバーヘッドな双方向接続を実現する。
その明快さは同時に限界でもある。WebSocketsは、消費者がブラウザでない場合、データが構造化されている場合、ボリュームが大きい場合、そしてWebSocketsが決して約束していないもの(型付きスキーマ、多重化されたストリーム、デッドライン伝播、バックプレッシャー)が必要な場合に苦戦する。それを解決するために作られたのがgRPCだ。
gRPCはどのように機能するか
gRPCは設計空間の反対側から出発する。webhookがHTTPコールバックで、WebSocketsがTCP上の薄いフレーミング層であるのに対し、gRPCは中核に型付き契約を持つ完全なRPCフレームワークだ。ビジネスロジックを書く前に、サービスとそれが送受信するメッセージを定義する.protoファイルを書く。protobufコンパイラがこのファイルを、自分の言語(スタブと呼ばれる)による生成済みのクライアントコードとサーバーコードに変換し、リモートメソッドの呼び出しがコード上ではローカル関数の呼び出しのように見えるようにする。
service Geyser {
rpc Subscribe(stream SubscribeRequest)
returns (stream SubscribeUpdate);
}トランスポート層はHTTP/2で、シリアライズにはProtocol Buffersが組み合わされる。両者が合わさることで、gRPCは典型的なREST + HTTP/1.1 + JSONスタックに対して3つの優位性を持つ。
- 多重化により、HTTP/1.1の問題であるヘッドオブラインブロッキング(1つの遅いレスポンスが同一接続上の後続リクエストすべてを詰まらせる問題)が解消される。gRPCでは、各呼び出しが同一のTCP接続を共有する独立したHTTP/2ストリームとして動作する。Squareは、接続オーバーヘッドを削減するために内部のサービス間トラフィックをgRPCに移行した多くのエンジニアリングチームの一つだ。
- ヘッダー圧縮(HPACK)は、繰り返されるヘッダーをコンパクトな参照に置き換える。RPCトラフィックでは同じメソッド、コンテンツタイプ、認証ヘッダーが呼び出しごとに繰り返されるため、これによってヘッダーのオーバーヘッドが85〜90%削減される。
- Protocol Buffersはメッセージをテキストではなくコンパクトなバイナリにシリアライズする。ペイロードは非圧縮環境でJSONより34%小さく、デシリアライズは言語によっては最大6倍高速になる。トレードオフとして、バイナリペイロードは人間が読める形式ではないため、トラフィックを確認するにはgrpcurlやPostmanのようなツールが必要になる。
gRPCは4種類のストリーミングモードをサポートする。単項(1リクエスト、1レスポンス)は通常のAPI呼び出しのように動作する。サーバーストリーミング(1リクエスト、複数レスポンス)はフィードやデータプッシュに向く。クライアントストリーミング(複数リクエスト、1レスポンス)はバッチアップロードを処理する。双方向ストリーミング(両側が独立して送信)はリアルタイムでステートフルなやり取りを実現する。
gRPCは汎用プロトコルではない。両端を自分で制御できるマシン間で構造化データを高頻度で移動させるバックエンドサービスに狭く最適化されており、その集中したニッチにおいてこそ、スキーマを書きコード生成を実行する初期コストが見合う。gRPCがこの地位を得ているのは、バイナリエンコーディングだけが理由ではなく、プロトコルに組み込まれた本番向けのプリミティブが理由でもある。デッドライン伝播は、すべてのタイムアウトを呼び出しチェーン全体を通過する絶対的な時点に変換する。サービスAが5秒のデッドラインを設定してサービスBを呼び出し、サービスBがサービスCを呼び出す場合、各ホップは残り時間を正確に把握できる。デッドラインが切れると、下流の作業はすべてキャンセルされ、リソースが解放される。フロー制御はHTTP/2から継承されており、消費側が遅い場合、送信側は自動的に速度を合わせて落とす。これは組み込みのバックプレッシャーであり、webhookもWebSocketsもネイティブでは提供していない。
Solanaでは、gRPCが利用可能な最高性能のデータストリーミングを支えている。Yellowstone gRPCプラグイン(Geyser)はバリデーターのメモリ空間に直接ロードされ、ディスクに書き込まれる前にアカウントやトランザクションの更新を捕捉し、protobufとしてシリアライズし、永続的なHTTP/2ストリームを通じてプッシュする。
3つのプロトコルはパフォーマンス面でどう比較されるか
スループットとレイテンシーのベンチマークは、サービス間通信について明確な線を引く。バイナリのprotobufエンコーディングとHTTP/2の多重化が組み合わさることで、gRPCはREST over HTTP/1.1よりも計測可能なほど高いスループットと低いレイテンシーを実現する。特にペイロードが小さく同時呼び出し数が多いワークロードで顕著だ。
WebSocketsは別の点で勝る。ブラウザ-サーバー間ストリーミングにおけるメッセージあたりのオーバーヘッドの低さだ。初回のハンドシェイクの後、各メッセージが運ぶフレーミングのオーバーヘッドは2〜6バイトで、ブラウザのネイティブWebSocket APIがプロキシやビルドツールなしに接続を扱う。
webhookはパフォーマンス向けに設計されていない。各配信は、完全な接続確立オーバーヘッドを伴う独立したHTTPリクエストだ。webhookが解決するのは別の問題、すなわちスループットよりも単純さと互換性を重視した、信頼性のある非同期イベント通知だ。
ブロックチェーン固有の数値もこの傾向を裏付けている。SolanaのYellowstone gRPCはスロットレイテンシー約5msでデータをストリーミングし、ネイティブのWebSocketsは約10ms、RPCポーリングは約150msと遅れをとる。プロトコルの複雑さが一段上がるごとに、計測可能な速度向上が得られる。
各プロトコルはどこで破綻するか
webhookは高頻度イベントで苦戦する。毎秒数百件の配信になると、コールバックごとのHTTPオーバーヘッドがボトルネックになる。バーストイベントはサンダリングハード問題を引き起こす。大規模なオンチェーンイベントが数百万件のwebhook配信を同時に引き起こし、プロバイダーのリトライキューと受信側のエンドポイントの両方を圧倒する。webhookはまた厳密に一方向だ。双方向通信が必要なユースケースには別のプロトコルが必要になる。webhookインフラを構築したあるエンジニアが述べているように、「webhookの運用はつらい」。
WebSocketsは規模が拡大すると苦戦する。各接続はサーバーリソースを保持する(アイドル時2〜10 KB、稼働時はさらに多い)。チューニングされたサーバーは50万以上のアイドル接続を扱えるが、本当のボトルネックは接続の入れ替わりだ。TLSハンドシェイクはCPUコア1つあたり毎秒1,000〜3,000件を消費する。サーバーが再起動すると、接続していたクライアントすべてが同時に再接続し、再接続の嵐が全体障害へと連鎖しうる。Solanaでは、標準的なWebSocket実装が「負荷下で不安定になり」、パフォーマンスが低下し頻繁に切断が発生したため、Solanaチームはこれを機にgRPCへと移行した。
gRPCはブラウザの端で苦戦する。ブラウザはネイティブでgRPCを話せない。gRPC-Webプロトコルはプロキシ(通常Envoy)を介してこの隙間を埋めるが、その過程でクライアントストリーミングと双方向ストリーミングを失う。バイナリのprotobufペイロードはcurlやブラウザのDevToolsで確認できないため、開発者体験が重要な公開向けAPIにはgRPCは向いていない。protobufのツールチェーン(スキーマ定義、コード生成、ビルド統合)は、単純な統合にとっては無視できないセットアップコストになる。
webhooks、WebSockets、gRPCはどう選ぶべきか
まずユースケースが求める通信パターンから考える。
webhookがデフォルトの選択肢だ。バックエンドが離散的なイベント(トランザクションの確定、NFTの転送、支払いの完了)の発生を知る必要があり、イベントレートが毎秒数百件以下であれば、webhookはどの言語、どのフレームワーク、どのファイアウォールでも動作する最も低コストな統合方法だ。永続接続もストリーミングインフラもクライアントSDKも不要だ。
イベントレートが上がる、あるいはデータが双方向に流れる必要がある場合は、WebSocketsが担う。ライブ価格ティッカー、オーダーブックの更新、メンプール監視、共同編集ダッシュボードなど、ブラウザへの永続接続がAPIへの過剰なアクセスを避ける場面を思い浮かべればよい。ネイティブなブラウザサポート、プロキシ不要、バイト単位のフレームオーバーヘッドだ。
バックエンドサービス間で最大限のスループットが必要な場合はgRPCを選ぶ。インデクサーのパイプライン、バリデーターデータのストリーミング、マイクロサービス間通信、そして接続の両端を自分で制御できるワークロードは、バイナリシリアライゼーション、多重化されたストリーム、型付き契約、組み込みのバックプレッシャーから恩恵を受ける。ネックとなるのはツールチェーンだ。protobufスキーマ、コード生成、ネイティブなブラウザサポートの欠如。パフォーマンスと信頼性がセットアップの複雑さを上回るなら、その価値はある。
アーキテクチャが求めるなら、これらを組み合わせる。多くの本番システムは3つすべてを使う。gRPCがバリデーターからインデクサーへデータをストリーミングし、WebSocketsが処理済みデータをブラウザダッシュボードにプッシュし、webhookが離散イベントを外部システムに通知する。単一のプロトコルにすべてを賭ける理由はない。
Alchemyでリアルタイムのブロックチェーンデータを扱う
当社は100以上のチェーンにわたって3つすべてのプロトコルをサポートしている。
当社のCustom Webhooksは、トランザクション確定、アドレスアクティビティ、NFTイベントを、HMAC署名検証と自動リトライ付きのHTTP POSTコールバックとして配信する。GraphQLフィルタを使えば、アプリケーションに必要なコントラクトイベントだけを正確に購読できる。
Smart WebSocketsは、フィルタ済みのeth_subscribeストリームを、自動再接続とレイテンシー削減付きで、リアルタイムのフロントエンドアプリケーション向けに提供する。
大量データを処理するSolanaチーム向けに、当社のYellowstone gRPCストリーミングは、バリデーターのメモリから直接アカウント更新とトランザクションを10ms未満のレイテンシーで配信する。
3つすべて無料プランで利用できる。契約もウェイトリストも不要だ。サインアップして数分でストリーミングを始めよう。
よくある質問
webhooks、WebSockets、gRPCの主な違いは何か
webhookはサーバー間のイベント通知向けの一方向HTTPコールバックであり、WebSocketsはクライアントとサーバー間の永続的な双方向接続を提供し、gRPCは型付き契約を備えた高スループットのサービス間通信向けに設計されたHTTP/2上のRPCフレームワークだ。
WebSocketsやgRPCの代わりにwebhookを使うべきなのはどんな時か
トランザクション確定やNFT転送のような離散的なイベント通知が毎秒数百件未満のレートで必要で、永続接続を必要としない場合はwebhookを使う。専用のクライアントSDKやインフラを必要としない、最も単純な統合方法だ。
WebSocketsがより良い選択肢になるのはどんな時か
WebSocketsは、ライブ価格ティッカー、オーダーブックの更新、ダッシュボードなど、ブラウザクライアントへの継続的なリアルタイムデータストリーミングに最適だ。最初のハンドシェイクの後は、ネイティブなブラウザサポートと低いメッセージあたりのオーバーヘッド(2〜6バイト)を提供する。
gRPCはどのような問題を解決するために設計されているか
gRPCは、インデクサーのパイプラインやバリデーターデータのストリーミングなど、構造化データを高頻度で移動させるバックエンドサービス向けに最適化されている。バイナリシリアライゼーション、多重化されたストリーム、Protocol Buffersによる型付き契約、組み込みのバックプレッシャー、デッドライン伝播を提供する。
webhooks、WebSockets、gRPCを同じシステム内で組み合わせて使えるか
はい、多くの本番システムはこの3つすべてを組み合わせている。バックエンドのサービス間通信にはgRPC、ブラウザへのリアルタイム更新にはWebSockets、外部システムへの離散イベント通知にはwebhookを使う。それぞれのプロトコルが異なるアーキテクチャ層を解決する。
これらのプロトコル間のパフォーマンスの違いは何か
gRPCはバイナリエンコーディングとHTTP/2の多重化により最も低いレイテンシーと最も高いスループットを実現し、WebSocketsはブラウザストリーミング向けに低いオーバーヘッド(メッセージあたり2〜6バイト)を提供し、webhookは完全なHTTPラウンドトリップのオーバーヘッドのためメッセージあたりのコストが最も高いが、パフォーマンスよりも単純さを優先する。
各プロトコルの主な欠点は何か
webhookは高頻度イベントとバーストトラフィックに苦戦し、WebSocketsは接続の入れ替わりとサンダリングハード型の再接続の嵐によるスケーリング上の課題に直面し、gRPCはprotobufツールチェーンのセットアップが必要で、ネイティブなブラウザサポートを欠き、Webクライアント向けにはプロキシ(gRPC-Web)が必要になる。
Alchemyはブロックチェーンアプリケーション向けにこれらのプロトコルをどのようにサポートしているか
Alchemyは、100以上のチェーンにわたるイベント通知向けのCustom Webhooks、自動再接続付きのフィルタ済みeth_subscribeストリーム向けのSmart WebSockets、そしてバリデーターのメモリから直接10ms未満のレイテンシーでアカウント更新を配信するSolana向けのYellowstone gRPCストリーミングを提供している。
関連する概要

ブロックチェーンで魔法を生み出す
Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。


