Thursday, January 15, 2015

[メモ] TCP上(もしくはHTTP)にリトライ可能なアプリケーションプロトコルを実現する方法

HTTP/1.1の持続的接続においては、サーバがリクエストを受け取ったあとに異常終了したのか、リクエストを受け取らずに接続を閉じたのか判別することができない。このため、べき等性の保証がないアプリケーションにおいて、リトライを行うべきか否か自動的に判断できなくなる場合がしばしば発生する

リトライ可能か否か(ピアがメッセージの処理を開始した否か)を判別するには、より細かな情報交換を行う別種のプロトコルを採用しても良いが、複雑なプトロコルはパフォーマンスに悪影響を及ぼす可能性が高いので避けたいところである。

というわけで、以下本題。

pipeliningを行わないHTTP/1.1のような単純なリクエスト/レスポンス型プロトコルをそのままに、アプリケーションレイヤへのリクエスト到達可否を判定する手軽な方法としては、SO_LINGERを用いる方法がある。具体的には、以下のような形式でサーバを実装する。
while (1) {
  receive_request();
  use_RST_for_close(); // SO_LINGERを使い、RSTで切断するよう設定
  handle_request();
  send_response();
  use_graceful_close(); // SO_LINGERを使い、graceful closeを行うよう設定
}
クライアントサイドでは、リクエスト送信後にEPIPE(write(2)の場合)かECONNRESET(read(2)の場合)を受け取った場合のみ、リクエストを再送すればよい。

別解としては、サーバが接続を切断する際に「HTTP/1.1 599 Going Away」のようなレスポンスを(たとえリクエストを受信していなくとも)送信するという方法が考えられる(この場合はlingering closeを行わない)。クライアントは、サーバからこのレスポンスを受信した場合のみ、リクエスト再送を行えば良い。

追記: H2Oでは後者の方式をサポートしようかと考えている。そうすれば「Docker と SO_REUSEPORT を組み合わせてコンテナのHot Deployにチャレンジ - blog.nomadscafe.jp」で挙げられているようなデプロイ手法において、(例示されている常にリトライする方法とは異なり)安全なホットデプロイが実現可能になる。

注: パイプライン処理については行わない前提で考える

4 comments:

  1. Thank you for another fantastic posting. Where else could anyone get that kind of information in such a perfect way of writing? I have a speech next week, and I was looking for more info.
    FRIV 2 | Juegos Friv | Friv 2 Planet | Play Friv 2 Games
    Juegos Friv - La Mejor Colección de Juegosfrivol
    Friv 4 Games - Friv 400 School for Kids

    ReplyDelete