Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18689] ファイルの概要

このコミットは、Go言語の net/http パッケージにおいて、http.Server にオプションの ConnState コールバック機能を追加するものです。これにより、HTTPサーバーへのクライアント接続の状態変化(新規接続、アクティブ、アイドル、ハイジャック、クローズ)を外部から監視し、それに応じた処理を柔軟に実装できるようになります。特に、サーバーのグレースフルシャットダウンポリシーの実装を容易にすることを目的としています。

コミット

commit 67b8bf3e32ec0bcc79453caeea9595a3ca036929
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Thu Feb 27 18:29:00 2014 -0800

    net/http: add optional Server.ConnState callback
    
    Update #4674
    
    This allows for all sorts of graceful shutdown policies,
    without picking a policy (e.g. lameduck period) and without
    adding lots of locking to the server core. That policy and
    locking can be implemented outside of net/http now.
    
    LGTM=adg
    R=golang-codereviews, josharian, r, adg, dvyukov
    CC=golang-codereviews
    https://golang.org/cl/69260044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/67b8bf3e32ec0bcc79453caeea9595a3ca036929

元コミット内容

net/http パッケージの http.Server に、接続状態を通知するオプションの ConnState コールバックを追加します。これにより、サーバーのコアに複雑なロック機構を追加することなく、様々なグレースフルシャットダウンポリシーを net/http の外部で実装できるようになります。

変更の背景

この変更は、GoのIssue #4674 に対応するものです。当時の net/http パッケージには、HTTPサーバーに接続しているクライアントの接続状態を外部から把握するメカニズムがありませんでした。これは、特にサーバーのグレースフルシャットダウン(サービスを停止する際に、既存のリクエスト処理を完了させ、新規の接続を受け付けないようにするプロセス)を実装する上で大きな課題となっていました。

グレースフルシャットダウンを実現するためには、サーバーが現在処理中のリクエストや、キープアライブ状態にあるアイドルな接続を把握し、それらが終了するのを待つ必要があります。しかし、net/http の内部でこれらの接続状態を管理し、外部に公開する仕組みがなかったため、ユーザーはサーバーの停止時に接続が完全にクローズされるのを待つことが困難でした。

このコミットの目的は、http.Server の内部に接続状態を監視するためのフックポイントを提供することです。これにより、ユーザーは ConnState コールバック関数を登録することで、接続が StateNewStateActiveStateIdleStateHijackedStateClosed のいずれかの状態に遷移した際に通知を受け取ることができます。この情報を用いて、アプリケーションレベルで接続の追跡や、グレースフルシャットダウン時の接続終了待機などのロジックを柔軟に実装することが可能になります。コミットメッセージにあるように、「特定のポリシー(例:lameduck期間)を選択することなく、またサーバーコアに多くのロックを追加することなく」この機能を提供することが重視されています。

前提知識の解説

Go言語の net/http パッケージ

net/http パッケージは、Go言語でHTTPクライアントとサーバーを実装するための標準ライブラリです。ウェブアプリケーションやAPIサーバーを構築する際に中心的な役割を果たします。

  • http.Server: HTTPサーバーの構成と動作を制御する構造体です。リスニングアドレス、ハンドラ、タイムアウト設定などを持ちます。
  • http.Handler: HTTPリクエストを処理するためのインターフェースです。ServeHTTP(ResponseWriter, *Request) メソッドを実装することで、カスタムのHTTPハンドラを作成できます。
  • HTTP接続のライフサイクル: HTTP/1.1では、パフォーマンス向上のために「キープアライブ」接続が一般的に使用されます。これは、一つのTCP接続上で複数のHTTPリクエスト/レスポンスのやり取りを行うことを可能にします。接続は、リクエストの処理中(アクティブ)、リクエスト処理後に次のリクエストを待っている状態(アイドル)、または完全にクローズされた状態など、様々な状態を遷移します。
  • HTTPコネクションのハイジャック (Hijacking): net/http パッケージの http.Hijacker インターフェースを通じて提供される機能です。これにより、HTTPハンドラが基盤となるTCP接続の制御を完全に奪い取ることができます。これは、WebSocketのようなHTTPプロトコル以外のプロトコルに切り替える場合や、カスタムのプロトコルを実装する場合に利用されます。接続がハイジャックされると、net/http パッケージはその接続の管理から手を引きます。
  • グレースフルシャットダウン: 稼働中のサーバーを停止する際に、現在処理中のリクエストが中断されないようにし、新しいリクエストの受け入れを停止するプロセスです。これにより、ユーザーへの影響を最小限に抑え、データの整合性を保ちながら安全にサービスを停止できます。

コールバック関数

コールバック関数とは、あるイベントが発生したときにシステムによって呼び出される関数のことです。このコミットでは、接続の状態が変化したときに http.Server がユーザー定義の関数を呼び出すようにすることで、ユーザーがその変化に対応できるようになります。

技術的詳細

このコミットの主要な変更点は、net/http/server.goServer.ConnState フィールドと ConnState 型、および関連する定数を追加したことです。

http.Server 構造体への ConnState フィールドの追加

type Server struct {
	// ... 既存のフィールド ...

	// ConnState specifies an optional callback function that is
	// called when a client connection changes state. See the
	// ConnState type and associated constants for details.
	ConnState func(net.Conn, ConnState)
}

Server 構造体に ConnState という関数型のフィールドが追加されました。このフィールドは、net.Conn(基盤となるネットワーク接続)と ConnState(接続の状態を表すカスタム型)を引数にとる関数を期待します。ユーザーがこのフィールドにコールバック関数を設定すると、接続の状態が変化するたびにその関数が呼び出されます。

ConnState 型と関連定数の定義

// A ConnState represents the state of a client connection to a server.
// It's used by the optional Server.ConnState hook.
type ConnState int

const (
	// StateNew represents a new connection that is expected to
	// send a request immediately. Connections begin at this
	// state and then transition to either StateActive or
	// StateClosed.
	StateNew ConnState = iota

	// StateActive represents a connection that has read 1 or more
	// bytes of a request. The Server.ConnState hook for
	// StateActive fires before the request has entered a handler
	// and doesn't fire again until the request has been
	// handled. After the request is handled, the state
	// transitions to StateClosed, StateHijacked, or StateIdle.
	StateActive

	// StateIdle represents a connection that has finished
	// handling a request and is in the keep-alive state, waiting
	// for a new request. Connections transition from StateIdle
	// to either StateActive or StateClosed.
	StateIdle

	// StateHijacked represents a hijacked connection.
	// This is a terminal state. It does not transition to StateClosed.
	StateHijacked

	// StateClosed represents a closed connection.
	// This is a terminal state. Hijacked connections do not
	// transition to StateClosed.
	StateClosed
)

ConnState という新しい整数型が定義され、接続の様々な状態を表すための定数(StateNew, StateActive, StateIdle, StateHijacked, StateClosed)が導入されました。これらの定数は、コールバック関数に渡される ConnState 引数の値として使用されます。

  • StateNew: 新しい接続が確立された直後の状態。リクエストを送信する準備ができています。
  • StateActive: 接続がリクエストの読み込みを開始し、ハンドラに渡される前の状態。リクエストが処理されている間はこの状態が続きます。
  • StateIdle: リクエストの処理が完了し、キープアライブ状態にある接続が、次のリクエストを待っている状態。
  • StateHijacked: 接続がハイジャックされた状態。この状態になると、net/http パッケージは接続の管理を停止します。これは終端状態であり、StateClosed には遷移しません。
  • StateClosed: 接続がクローズされた状態。これも終端状態です。ハイジャックされた接続は StateClosed には遷移しません。

また、ConnState 型には String() メソッドが追加され、デバッグやログ出力のために状態名を文字列として取得できるようになっています。

conn 構造体と serve() メソッドでの setState の利用

net/http/server.go 内の conn 構造体(個々のHTTP接続を管理する内部構造体)に、setState というヘルパーメソッドが追加されました。

func (c *conn) setState(nc net.Conn, state ConnState) {
	if hook := c.server.ConnState; hook != nil {
		hook(nc, state)
	}
}

この setState メソッドは、Server.ConnState フィールドにコールバック関数が設定されている場合にのみ、その関数を呼び出します。

conn.serve() メソッド(個々のHTTP接続を処理するメインのループ)の様々な箇所で、この setState メソッドが呼び出されるようになりました。これにより、接続がライフサイクルの異なる段階に到達するたびに、登録された ConnState コールバックがトリガーされます。

  • StateNew: conn.serve() の開始時、接続が確立された直後に呼び出されます。
  • StateActive: リクエストの読み込みが開始された直後(readRequest() の後)に呼び出されます。
  • StateHijacked: 接続がハイジャックされた場合に呼び出されます。
  • StateIdle: リクエストの処理が完了し、接続がキープアライブ状態になった場合に呼び出されます。
  • StateClosed: 接続がクローズされる直前(c.close() の後)に呼び出されます。

これらの変更により、net/http パッケージの内部で管理されている接続の状態変化が、外部のアプリケーションロジックに透過的に通知されるようになり、より高度な接続管理やグレースフルシャットダウンの実装が可能になりました。

コアとなるコードの変更箇所

src/pkg/net/http/server.go

  • Server 構造体に ConnState func(net.Conn, ConnState) フィールドを追加。
  • ConnState 型(int のエイリアス)と、StateNew, StateActive, StateIdle, StateHijacked, StateClosed の定数を追加。
  • ConnState 型に String() メソッドを追加し、状態名を文字列で返すようにする。
  • conn 構造体に setState(nc net.Conn, state ConnState) ヘルパーメソッドを追加。
  • conn.serve() メソッド内で、接続のライフサイクルに応じて setState を呼び出す箇所を追加:
    • StateNew: conn.serve() の開始時。
    • StateActive: リクエストの読み込み後。
    • StateHijacked: 接続がハイジャックされた時。
    • StateIdle: リクエスト処理完了後、キープアライブ状態へ移行時。
    • StateClosed: 接続がクローズされる直前。

src/pkg/net/http/serve_test.go

  • TestServerConnState という新しいテスト関数を追加。
  • このテストでは、httptest.NewUnstartedServer を使用してテストサーバーをセットアップし、ts.Config.ConnState にカスタムのコールバック関数を設定。
  • コールバック関数内で、接続IDと状態をログに記録し、期待される状態遷移シーケンスと比較することで、ConnState コールバックが正しく機能していることを検証。
  • 通常のHTTPリクエスト、Connection: close ヘッダを持つリクエスト、および接続をハイジャックするリクエストのそれぞれで状態遷移をテスト。

コアとなるコードの解説

src/pkg/net/http/server.go の変更

Server 構造体への ConnState フィールドの追加は、この機能の公開APIとなります。ユーザーはこのフィールドに独自の関数を割り当てることで、接続状態の通知を受け取ることができます。

ConnState 型と関連定数は、接続の状態を明確に定義し、コールバック関数に渡される情報の意味を標準化します。String() メソッドは、デバッグ時の可読性を高めます。

conn.setState ヘルパーメソッドは、Server.ConnState が設定されている場合にのみコールバックを呼び出すことで、オーバーヘッドを最小限に抑えます。

conn.serve() メソッド内の setState の呼び出しは、HTTP接続の処理フローの重要なポイントに戦略的に配置されています。

  • StateNew は、新しいTCP接続が確立され、HTTPサーバーがその接続の処理を開始したことを示します。
  • StateActive は、クライアントからHTTPリクエストのデータが読み込まれ始めたことを示します。これは、リクエストがハンドラに渡される直前です。
  • StateIdle は、HTTPリクエストの処理が完了し、レスポンスが送信された後、接続がキープアライブのために開いたままになっている状態を示します。サーバーは次のリクエストを待っています。
  • StateHijacked は、ハンドラが基盤となるTCP接続の制御を奪い取ったことを示します。この時点で、net/http パッケージはそれ以上その接続を管理しません。
  • StateClosed は、接続が完全にクローズされたことを示します。

これらの状態遷移を正確に通知することで、外部のロジックは接続のライフサイクル全体を追跡し、例えば、現在アクティブな接続の数をカウントしたり、アイドルな接続をタイムアウトさせたり、サーバーシャットダウン時にすべての接続が終了するのを待ったりするなどの高度な制御が可能になります。

src/pkg/net/http/serve_test.go の変更

TestServerConnState は、この新機能の動作を検証するための重要なテストです。httptest.NewUnstartedServer を使用することで、実際のHTTPサーバーを簡単に起動し、その Config をカスタマイズして ConnState コールバックを設定できます。

テスト内で sync.MutexstateLog を使用して、コールバックが呼び出された順序と状態を記録しています。これにより、期待される状態遷移シーケンス(StateNew -> StateActive -> StateIdle -> StateActive -> StateClosed など)と実際のログを比較し、機能が正しく実装されていることを確認しています。

特に、Connection: close ヘッダを持つリクエストや、Hijack を使用するリクエストのテストケースが含まれていることは重要です。これらは、HTTP接続の特殊なケースであり、ConnState コールバックがこれらのシナリオでも正しく動作することを確認しています。StateHijacked が終端状態であり、StateClosed に遷移しないこともテストで検証されています。

このテストは、ConnState コールバックが提供する情報の正確性と信頼性を保証するための基盤となります。

関連リンク

参考にした情報源リンク

これらの情報源は、Server.ConnState の目的、使用方法、およびGoにおけるグレースフルシャットダウンの一般的なプラクティスを理解する上で役立ちました。