第6章: ルーティング

サーバーがどこにあるかを知ることは、通信の問題の半分にすぎません。残りの半分はリクエストをそのサーバーに効率的かつ確実に、そしてサーバーを圧倒することなく届けることです。ルーティングサービスはディスカバリ(サーバーの発見)と通信(データの交換)の間のギャップを埋め、コネクションプーリング、負荷分散、クライアントのためのクリーンな抽象化を追加します。

第1章でルーティングサービスを紹介しました。ここではその実装を詳しく見ていきます:TCPソケットを再利用するコネクションプール、並行性を制御するセマフォ、スタンドアロンプロキシまたは組み込みライブラリとして実行できるデュアルモードアーキテクチャです。

インターフェース

routing/src/lib.rs ルーティングのインターフェースは単一のプロシージャで構成されます:route。クライアントはターゲットシステムの名前、呼び出すプロシージャ、ペイロードを指定します。ルーティングサービスはサーバーの発見、コネクション管理、リクエストの転送のすべての複雑さを処理します:

pub const ROUTE_PROCEDURE: ProcedureId = 1;

#[derive(Debug, Serializable, Deserializable)]
pub struct RouteArgs {
    pub name: String,
    pub procedure_id: i32,
    pub payload: String,
}

#[derive(Debug, Serializable, Deserializable)]
pub struct RouteResult {
    pub payload: String,
}

コネクションプール

TCP接続の確立には3ウェイハンドシェイクが必要でリクエストごとにレイテンシーが追加されます。コネクションプーリングは確立済みの接続を複数のリクエスト間で再利用することでこのオーバーヘッドを排除します。セマフォが並行性制御の鍵で、最大数の同時リクエストが飛行中の場合追加のリクエストはブロックされます。

デュアルモードアーキテクチャ

ルーティングシステムは2つのモードで動作でき異なるユースケースに異なるトレードオフを提供します。

プロキシモードはルーティングをスタンドアロンサーバーとして実行します。クライアントはリクエストをルーティングサーバーに送信し、ルーティングサーバーが適切なバックエンドに転送します。

ライブラリモードはルーティングをクライアントプロセスに直接組み込みます。Router構造体は独自のプールを維持し仲介なしでコネクションを管理します。プロキシを通じた追加のネットワークホップが排除されレイテンシーが削減されますが、分散型のコネクション管理になります。

設計の議論

プロキシモードとライブラリモードの選択はデプロイメント環境に依存します。プロキシはクライアントにとってシンプルで(プロキシのアドレスだけ知ればよい)可観測性とポリシー適用の単一ポイントを提供します。ライブラリはより高速で(追加ホップなし)より回復力があります(単一障害点なし)。多くの本番システムは両方を使用します。

ディスカバリとともにルーティングサービスはプラネタリスケールコンピュータの通信バックボーンを提供します。ディスカバリはサーバーがどこにあるかを知り、ルーティングはそれらと効率的に通信する方法を知ります。この関心の分離により各サービスは独立して進化しながらすべてのサービス間通信の信頼性の高い基盤を提供できます。