libpq 通信プロトコルは、TCP/IP プロトコルに基づく一連のメッセージ通信プロトコルであり、psql、JDBC、PgAdmin などのクライアント プログラムが PostgreSQL バックエンド サーバーにクエリを送信し、返されたクエリ結果を受信できるようにします。
今回のライブブロードキャストでは、libpq通信プロトコルの実装原理と実行メカニズムを紹介しましたが、以下の内容はライブブロードキャストのテキストに沿って構成されています。
libpq 通信プロトコルの概要
通信プロトコルとは、制御情報の交換規則、つまりネットワーク上の伝送規則に従った通信当事者間の一連の規格および取り決めを指します。2 つのエンティティが正常に通信するには、「同じ言語を話す」必要があります。
libpq プロトコルは、TCP/IP モデルのネットワーク階層化におけるアプリケーション層プロトコルの一種です。libpqプロトコル通信を行う前に、コネクションの確立が完了している必要があります。libpq プロトコルは、対話に必要な認証ハンドシェイク プロセス、データ要求応答プロセス、およびエラー処理プロセスを記述します。
インタラクション レベルの観点から見ると、libpq 通信プロトコルには、接続の確立、データのクエリ、および接続の終了という 3 つのステージが含まれます。次に、これら 3 つのステージのさまざまな状態とモードについて詳しく説明します。
接続確立フェーズ(起動フェーズ)
ユーザーは libpq ドライバーを使用してデータベースとの接続を作成し、認証情報を送信します。すべてが正常であれば、サーバーはステータス情報をフィードバックし、接続が正常に確立されます。
クライアント接続とサーバー受け入れプロセスの概略図
上図に示すように、libpq のコネクション確立フェーズは、コネクション確立フェーズ、暗号化ネゴシエーションフェーズ、認証ネゴシエーションフェーズの 3 段階に大別されます。対応するコード実装は、connect 関数と accept 関数です。暗号化ネゴシエーションと認証ネゴシエーションは、libpq 通信の接続確立フェーズにおける重要なプロセスです。
接続を確立する
ユーザーが libpq を使用して接続を作成するプロセスは、コード段階から主に 3 つのステップに分かれています。
1. PGconn タイプの接続オブジェクト conn を作成します。
2. データベースに接続します (インターフェイス PQconnectdb /PQconnectdbParams/ PQsetdbLogin 経由)。
3. 接続オブジェクト conn のステータスを確認します。CONNECTION_OK であれば、接続は成功です。
libpq を介してユーザーとサーバー間の接続を確立するプロセスには、主に次の 2 種類の状態が含まれます。
ポーリング ステータス タイプ PostgresPollingStatusType と接続ステータス タイプ ConnStatusType。
- 投票ステータス:
ポーリングは主に、conn オブジェクトがソケットを作成するのを待つ、接続パラメーターを書き込む、サーバーが結果を返すのを待つ、接続認証を待つなどの目的で使用されます。この処理では conn オブジェクトは作成されていますが、サーバーとのサービス接続処理は完了していません。
接続は即座に完了するわけではなく一連の処理が必要ですが、この処理における待機処理はポーリングという短い処理で制御されます。PGRES_POLLING_FAILED と PGRES_POLLING_OK はポーリング終了のステータス条件であり、PGRES_POLLING_READING と PGRES_POLLING_WRITING は現在の接続を要求し続ける必要があるステータス条件です。
空の conn オブジェクトが確立されると、ポーリングは初期状態 PGRES_POLLING_WRITING になり、PQconnectPoll を呼び出して、conn の確立の完了を待つ必要があるかどうかを尋ねます。それでも待機する必要がある場合は、接続が確立されるまで待機してステータスをポーリングし続けます。確立されない場合は、エラーが返されます。
- 接続ステータス:
PQconnectPoll 関数は、クライアント接続の接続ステート マシンをプッシュし、CONNECTION_MADE 状態を処理します。ここでの主なジョブは、認証リクエストを開始することです。
接続プロセス中はいつでも、PQstatus を呼び出すことで接続のステータスを確認できます。この時点で呼び出しが CONNECTION_BAD を返した場合は、接続プロセスは失敗しています。呼び出しが CONNECTION_OK を返した場合は、接続の準備ができています。どちらの状態も PQconnectPoll の戻り値から検出できます。
非同期接続プロセス中 (期間中のみ)、書き込み (クライアントが認証およびネゴシエーション情報をサーバーに送信したいことを意味します)、読み取り (読み取りはサーバーがパケットを返すのを待機していることを意味します) など、他の状態も表示される場合があります。
暗号化ネゴシエーション
暗号化ネゴシエーションフェーズは接続確立後の最初のフェーズであり、その後の認証ネゴシエーションフェーズでセッション情報が漏洩しないように、最初に接続を暗号化する必要があります。
暗号化ネゴシエーション フェーズはオプションであり、GSSAPI 認証または SSL 認証が有効な場合にのみ実行されます。この段階で、クライアントが PQconnectPoll 関数を呼び出すと、ConnStatusType 接続ステータスは CONNECTION_NEEDED になり、次に connect 関数を呼び出してサーバーに接続すると、接続ステータスは CONNECTION_STARTED に変わります。
この時点で、サーバー ポストマスターは次の呼び出しを実行します。StreamConnection 関数はサーバー ポートを使用してクライアントとの新しい接続を作成し、port->sock を新しい接続の FD として設定します。接続が正常に作成された後、ポストマスターは BackendStartup を呼び出して、クライアント接続用の postgres バックエンド サービス サブプロセスを作成します。
次に、PQconnectPoll 関数は、この接続のステート マシンを進め、CONNECTION_MADE 状態を処理し、認証要求を開始し、スタートアップ パッケージを構築しようとします。
コンパイルマクロパラメータ ENABLE_GSS または USE_SSL が有効な場合、暗号化ネゴシエーションが実行されます。
暗号化ネゴシエーションのプロセスは次のとおりです。
client->server(协议版本信息)
暗号化ネゴシエーションフェーズは接続確立後の最初のフェーズであり、その後の認証ネゴシエーションフェーズでセッション情報が漏洩しないように、最初に接続を暗号化する必要があります。
サーバー処理フロー:
ServerLoop->BackendStartup->BackendInitialize->ProcessStartupPacket(处理加密)
認証ネゴシエーション
暗号化ネゴシエーションフェーズが完了するかスキップされると、libpq プロトコルは認証フェーズを開始します。認証フェーズはスタートアップ メッセージで始まり、その形式はメッセージ長で始まり、その後にプロトコルのバージョン番号、次にキーと値のペアの形式の接続情報 (ユーザー名、データベース、その他の GUC パラメータと値など) が続きます。
フロントエンドがStartupメッセージを送信した後、バックエンドは認証応答を返します。認証応答メッセージの種類は「R」で、その内容は大きく3つの状況に分けられます。認証なし、ユーザーは現時点ではパスワードを確認する必要はありません)、認証方法と必須パラメータを指定すると、認証エラーが発生します。
フロントエンドは、認証応答情報によって提供される認証方法 (存在する場合) を介してバックエンドに認証リクエストを送信します。
認証要求メッセージには、パスワードやパスワードの MD5 値など、バックエンドで必要な認証パラメーターが含まれています。
認証エラー メッセージ ErrorResponse により、バックエンドは接続を直接閉じ、認証ネゴシエーションを停止します。
認証要求のタイプは「P」であり、その内容はコンテキストに応じて推測する必要があります。たとえば、前の認証応答メッセージの認証方式が MD5 の場合、認証要求メッセージの内容は MD5 値になります。パスワードの。
フロントエンドがバックエンドに認証要求を送信すると、認証が完了するか認証が失敗するまで、バックエンドは認証要求の内容に応じて再度応答します。したがって、認証フェーズの完了の兆候は、バックエンドが送信する内容は、認証完了の認証応答メッセージ、または ErrorResponse の認証エラー メッセージです。
認証が完了すると、バックエンドは認証応答メッセージの後に他のプロトコルを送信して、次のような必要なパラメーターをフロントエンドに通知します。
- タイプ "S" の ParameterStatus : パラメーター設定用のキーと値のペアです。
- タイプ "K" の BackendKeyData : リクエストをキャンセルするための Key を記述します。これは、開始段階でメイン ユーザーがキャンセル リクエストに必要とする Key 値であり、新しいセッションで別のセッションのブロック操作を中断するために使用されます。
- タイプ "Z" の ReadyForQuery : バックエンドが新しいデータ リクエストを開始する準備ができていることを表します。
これまでのところ、接続を確立するプロセスは完全に準備されています。接続を確立するための状態図は次のとおりです。
接続確立プロセスのロジック図
データクエリフェーズ(通常フェーズ)
データ クエリ フェーズでは、クライアントとサーバー間のすべての通信がメッセージ フローを通じて実行されます。メッセージの最初のバイトはメッセージ タイプを識別し、次の 4 バイトはメッセージ コンテンツの長さを識別し (長さにはこれら 4 バイト自体も含まれます)、特定のメッセージ コンテンツはメッセージ タイプによって決まります。
サーバーがサポートするメッセージ タイプは PostgresMain 関数で、クライアントがサポートするメッセージ タイプは pqParseInput3 関数です。データ クエリ フェーズでは、一般的に使用される 3 つの通信モード、つまり単純クエリ、拡張クエリ、およびデータのコピーがあります。
シンプル クエリ モード: クライアントはクエリ メッセージを通じてテキスト コマンドをサーバーに送信し、サーバーはリクエストを処理してクエリ結果を返します。クエリ結果には通常、構造とデータの 2 つの部分が含まれます。列名、タイプ OID、長さなどを含む構造は RowDescription メッセージを通じて渡され、データは DataRow メッセージを通じて渡され、各 DataRow メッセージにはデータ行が含まれます。
各コマンドの結果が送信された後、サーバーは現在のコマンドの実行が完了したことを示す CommandComplete メッセージを送信します。クライアントからのクエリ リクエストには複数の SQL コマンドが含まれる場合があります。各 SQL コマンドが実行されると、CommandComplete メッセージが返されます。クエリ リクエストが実行されると、ReadyForQuery メッセージが返され、新しいリクエストを送信できることがクライアントに通知されます。 。メッセージの流れは次のとおりです。
単純なクエリ メッセージ フロー図
拡張クエリ モード: 拡張クエリ プロトコルは、上記の単純なクエリの処理フローをいくつかのステップに分割し、各ステップは個別のサーバー メッセージによって確認されます。拡張クエリ プロトコルには通常、解析、バインド、記述、実行、同期の 5 つのステップが含まれますが、ここでは説明しません。
拡張クエリ プロトコルでは、サーバーのプリペアド ステートメント機能を使用できます。つまり、最初にパラメータ化された SQL を送信し、サーバーは受信後に SQL (ステートメント) を解析、書き換え、保存します。ここで保存されるステートメントがプリペアド ステートメントです。再利用可能; SQL 実行時に、事前に保存したプリペアドステートメント生成プランを直接取得して実行し、同じ種類の SQL の繰り返しの解析と書き換えを回避; その後、サーバーは適切な条件でプランをキャッシュしますその後の再利用のために。
PGQUERY_EXTENDED クエリ プロトコルは、SQL の実行プロセスを 3 つのレベルに分割します。ステートメントとポータル オブジェクトは、隣接する 2 つのレベル間で抽象化されます。各レベルでは個別の反復呼び出しが許可され、また、現在の接続のライフ サイクル内での反復呼び出しも許可されます。呼び出しにより SQL 実行プロセス全体が再利用可能になり、中間結果の保存により繰り返しの呼び出しが削減され、実行オーバーヘッドが節約され、実行速度が向上します。拡張クエリの完全なメッセージ フローを次の図に示します。
拡張クエリ メッセージ フロー図
データのコピー モード:データのインポート/エクスポートを効率的に行うために、libpq は Copy コマンドをサポートしています。コピー操作により、現在の接続がまったく異なるメッセージ通信方式に切り替わります。
データのコピーは 3 つのモードに対応します: コピーインはデータをインポートし、COPY FROM STDIN コマンドに対応します。コピーアウトはデータをエクスポートし、COPY TO STDOUT コマンドに対応します。コピー両方は、マスター間でバッチでデータを転送するために Walsender に使用されます。そしてバックアップ。
コピーインを例に挙げると、COPY コマンドを受信した後、サーバーは COPY モードに入り、CopyInResponse で応答します。次に、クライアントは Copydata メッセージを通じてデータを送信し、CopyComplete メッセージはデータ送信が完了したことを示します。メッセージを受信した後、サーバーは CommandComplete メッセージと ReadyForQuery メッセージを送信します。メッセージ フローを次の図に示します。
データコピーメッセージのフロー図
終了段階
この段階のプロセスは比較的単純で、クライアントのリクエストが終了した後、切断するためのメッセージをアクティブに送信できます。クライアントから終了メッセージを受信した後、サーバーはプロセスを直接終了します。
要約する
libpq を介して PostgreSQL との接続を確立するのは比較的複雑なプロセスであり、主に libpq が配置されているクライアント側によって駆動され、リクエストを開始して応答を待ちます。
接続確立ポーリング ステート マシン、接続確立プロセス ステート マシン、および環境変数設定ステート マシンでは、接続確立プロセスを完了するためにいくつかの状態で複数の遷移があります。接続の確立、暗号化ネゴシエーション、認証ネゴシエーションの 3 つの段階を経ると、PostgreSQL に接続された PGconn 接続オブジェクトが完成する準備が整い、アプリケーションはこのオブジェクトを通じて後続のさまざまなサービスを実行し、サーバーへのリクエストを開始し、解析して返すことができます。結果。
この共有では、libpq を使用して PostgreSQL サーバーとの接続を確立し、その接続を使用してビジネス リクエストを送信する方法を紹介します。libpq プロトコルに興味のある学生は、HashData パブリック アカウントをフォローして、libpq 通信プロトコルの技術的な詳細を学ぶことができます。