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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージのテストファイル src/pkg/net/http/serve_test.go に、TLS (Transport Layer Security) を使用したHTTPクライアントとサーバー間の並列ベンチマークを追加するものです。これにより、TLSが有効な状態でのHTTP通信のパフォーマンスを測定できるようになります。

コミット

commit 8c8bf3cc760e3cb806d94e2bf9a58664b8953108
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Fri Jun 27 18:30:09 2014 -0700

    net/http: add TLS benchmark
    
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/110080045

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

https://github.com/golang/go/commit/8c8bf3cc760e3cb806d94e2bf9a58664b8953108

元コミット内容

net/http: add TLS benchmark

R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/110080045

変更の背景

Go言語の net/http パッケージは、WebアプリケーションやAPIサーバーを構築する上で非常に重要なコンポーネントです。HTTP通信において、セキュリティは不可欠であり、TLS(旧SSL)はそのための標準的なプロトコルです。TLSは通信の暗号化、認証、データの完全性を提供しますが、その処理には計算コストが伴います。

このコミットが作成された背景には、TLSを介したHTTP通信のパフォーマンス特性をより詳細に理解し、最適化するための必要性があったと考えられます。既存のベンチマークはTLSを使用しないHTTP通信に焦点を当てていましたが、実際のプロダクション環境ではTLSが広く利用されています。そのため、TLSが有効な状態での net/http パッケージのパフォーマンスを測定し、潜在的なボトルネックを特定するための専用のベンチマークが求められていました。

特に、並列処理下でのTLSハンドシェイクや暗号化/復号化のオーバーヘッドは、システムの全体的なスループットに大きな影響を与える可能性があります。このベンチマークの追加により、開発者はTLSがパフォーマンスに与える影響を定量的に評価し、将来的な改善のための基礎データを得ることができるようになります。

前提知識の解説

1. Go言語のベンチマーク

Go言語には、標準ライブラリ testing パッケージにベンチマーク機能が組み込まれています。

  • go test -bench=. コマンドで実行されます。
  • Benchmark というプレフィックスを持つ関数がベンチマーク関数として認識されます。
  • ベンチマーク関数は *testing.B 型の引数を受け取ります。
  • b.N はベンチマークを実行する回数を示し、ループ内で処理を繰り返します。
  • b.ReportAllocs(): メモリ割り当ての統計を報告するように設定します。
  • b.ResetTimer(): タイマーをリセットし、ベンチマーク対象のコードの実行時間のみを測定するようにします。
  • b.SetParallelism(n): b.RunParallel で同時に実行されるゴルーチンの数を設定します。
  • b.RunParallel(func(pb *testing.PB)):pb.Next()true を返す間、並列に処理を実行します。これにより、複数のゴルーチンが同時にベンチマーク対象のコードを実行し、並行処理下でのパフォーマンスを測定できます。

2. net/http パッケージ

Goの net/http パッケージは、HTTPクライアントとサーバーの実装を提供します。

  • httptest.NewServer(handler): テスト目的でHTTPサーバーを起動します。ランダムなポートでリッスンし、テスト中にアクセスできるURLを返します。
  • httptest.NewUnstartedServer(handler): サーバーを起動せずに作成します。これにより、Start() または StartTLS() を呼び出す前にサーバーの設定を調整できます。
  • ts.Start(): HTTPサーバーを起動します。
  • ts.StartTLS(): HTTPSサーバーを起動します。自己署名証明書が自動的に生成され、TLS通信が有効になります。
  • http.Get(url string): 指定されたURLにGETリクエストを送信し、レスポンスを返します。
  • http.Client: HTTPリクエストを送信するためのクライアント構造体です。カスタムの Transport を設定することで、TLS設定などを細かく制御できます。
  • http.Transport: HTTPリクエストのラウンドトリップ(接続の確立、リクエストの送信、レスポンスの受信)を実装するインターフェースです。TLS設定は TLSClientConfig フィールドで指定します。

3. TLS (Transport Layer Security)

TLSは、インターネット上での通信を暗号化し、認証するためのプロトコルです。

  • 暗号化: 通信内容を第三者から読み取れないようにします。
  • 認証: サーバー(およびオプションでクライアント)が正当であることを確認します。
  • データの完全性: データが転送中に改ざんされていないことを保証します。
  • TLSハンドシェイク: クライアントとサーバーが安全な通信チャネルを確立するために行う一連のステップです。これには、証明書の交換、鍵の生成、暗号スイートのネゴシエーションなどが含まれます。
  • crypto/tls パッケージ: Go言語でTLSを扱うための機能を提供します。
  • tls.Config: TLS接続の設定を定義する構造体です。
    • InsecureSkipVerify: true に設定すると、サーバーの証明書検証をスキップします。これはテスト目的や自己署名証明書を使用する場合に便利ですが、本番環境ではセキュリティリスクがあるため推奨されません。

技術的詳細

このコミットの主要な変更点は、net/http/serve_test.go ファイル内のベンチマーク関数にTLSサポートを追加したことです。

  1. benchmarkClientServerParallel 関数の変更:

    • 既存の benchmarkClientServerParallel 関数に useTLS bool という新しい引数が追加されました。この引数によって、ベンチマークがTLSを使用するかどうかが制御されます。
    • サーバーの起動方法が変更されました。以前は httptest.NewServer を直接使用していましたが、変更後は httptest.NewUnstartedServer を使用してサーバーを初期化し、useTLS の値に応じて ts.StartTLS() または ts.Start() を呼び出すようになりました。これにより、TLSの有無を動的に切り替えられるようになりました。
  2. TLSクライアントの設定:

    • useTLStrue の場合、クライアントはカスタムの http.Transport を使用するように設定されます。
    • この TransportTLSClientConfig フィールドには、InsecureSkipVerify: true が設定された tls.Config が割り当てられます。これは、テスト環境で自己署名証明書を使用する場合に、証明書検証エラーを回避するためのものです。本番環境では、信頼できるCAによって署名された証明書を使用し、この設定を false にするか、デフォルトの検証を使用することが不可欠です。
    • クライアントは http.Client{Transport: noVerifyTransport} として初期化され、このカスタムクライアントを使用して client.Get(ts.URL) が呼び出されるようになります。これにより、TLSが有効なサーバーに対してHTTPリクエストが送信されます。
  3. 新しいベンチマーク関数の追加:

    • BenchmarkClientServerParallelTLS4BenchmarkClientServerParallelTLS64 という2つの新しいベンチマーク関数が追加されました。
    • これらの関数は、それぞれ4と64の並列度で benchmarkClientServerParallel を呼び出し、useTLS 引数を true に設定します。これにより、TLSが有効な状態で、指定された並列度でのHTTP通信のパフォーマンスが測定されます。

これらの変更により、Goの net/http パッケージのTLS通信におけるパフォーマンス特性を、並列処理の観点から詳細に分析できるようになりました。特に、TLSハンドシェイクのオーバーヘッド、暗号化/復号化のコスト、およびそれらが多数の同時接続にどのように影響するかを評価するための基盤が提供されます。

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

--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -2559,24 +2559,43 @@ func BenchmarkClientServer(b *testing.B) {
 }
 
 func BenchmarkClientServerParallel4(b *testing.B) {
-	benchmarkClientServerParallel(b, 4)
+	benchmarkClientServerParallel(b, 4, false)
 }
 
 func BenchmarkClientServerParallel64(b *testing.B) {
-	benchmarkClientServerParallel(b, 64)
+	benchmarkClientServerParallel(b, 64, false)
 }
 
-func benchmarkClientServerParallel(b *testing.B, parallelism int) {
+func BenchmarkClientServerParallelTLS4(b *testing.B) {
+	benchmarkClientServerParallel(b, 4, true)
+}
+
+func BenchmarkClientServerParallelTLS64(b *testing.B) {
+	benchmarkClientServerParallel(b, 64, true)
+}
+
+func benchmarkClientServerParallel(b *testing.B, parallelism int, useTLS bool) {
 	b.ReportAllocs()
-	ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+	ts := httptest.NewUnstartedServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
 		fmt.Fprintf(rw, "Hello world.\n")
 	}))
+	if useTLS {
+		ts.StartTLS()
+	} else {
+		ts.Start()
+	}
 	defer ts.Close()
 	b.ResetTimer()
 	b.SetParallelism(parallelism)
 	b.RunParallel(func(pb *testing.PB) {
+		noVerifyTransport := &Transport{
+			TLSClientConfig: &tls.Config{
+				InsecureSkipVerify: true,
+			},
+		}
+		client := &Client{Transport: noVerifyTransport}
 		for pb.Next() {
-			res, err := Get(ts.URL)
+			res, err := client.Get(ts.URL)
 			if err != nil {
 				b.Logf("Get: %v", err)
 				continue

コアとなるコードの解説

1. benchmarkClientServerParallel 関数のシグネチャ変更

func benchmarkClientServerParallel(b *testing.B, parallelism int, useTLS bool) {

この行は、既存のベンチマークヘルパー関数 benchmarkClientServerParalleluseTLS bool という新しいパラメータを追加しています。これにより、この関数がTLSを使用するかどうかを呼び出し元から制御できるようになります。

2. サーバーの起動方法の変更

	ts := httptest.NewUnstartedServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
		fmt.Fprintf(rw, "Hello world.\n")
	}))
	if useTLS {
		ts.StartTLS()
	} else {
		ts.Start()
	}

以前は httptest.NewServer を直接呼び出していましたが、ここではまず httptest.NewUnstartedServer を使用して、まだ起動していないサーバーインスタンスを作成しています。 その後の if useTLS ブロックで、useTLStrue の場合は ts.StartTLS() を呼び出してHTTPSサーバーとして起動し、false の場合は ts.Start() を呼び出して通常のHTTPサーバーとして起動します。これにより、単一のヘルパー関数でTLSの有無を切り替える柔軟性が得られます。

3. TLSクライアントの設定(b.RunParallel 内)

		noVerifyTransport := &Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}
		client := &Client{Transport: noVerifyTransport}

b.RunParallel の内部、つまり各並列ゴルーチン内で、TLSを使用する場合のクライアント設定が行われています。

  • noVerifyTransport という http.Transport のインスタンスが作成されます。
  • このトランスポートの TLSClientConfig フィールドに tls.Config が設定され、その中の InsecureSkipVerifytrue に設定されています。これは、テスト用の自己署名証明書を使用する際に、証明書検証エラーを回避するためのものです。
  • このカスタムトランスポートを持つ http.Client インスタンスが作成されます。

4. リクエストの送信方法の変更

		for pb.Next() {
			res, err := client.Get(ts.URL)

以前は http.Get(ts.URL) を直接呼び出していましたが、TLSが有効な場合は、上記で作成したカスタムクライアント client を使用して client.Get(ts.URL) を呼び出すように変更されています。これにより、TLS設定が適用されたHTTPリクエストが送信されます。

5. 新しいベンチマーク関数の追加

func BenchmarkClientServerParallelTLS4(b *testing.B) {
	benchmarkClientServerParallel(b, 4, true)
}

func BenchmarkClientServerParallelTLS64(b *testing.B) {
	benchmarkClientServerParallel(b, 64, true)
}

これらの新しい関数は、それぞれ4と64の並列度で benchmarkClientServerParallel を呼び出し、useTLS 引数を true に設定しています。これにより、TLSが有効な状態での並列ベンチマークが実行可能になります。既存の BenchmarkClientServerParallel4BenchmarkClientServerParallel64 も、useTLSfalse に設定するように変更されています。

これらの変更により、Goの net/http パッケージのTLS通信におけるパフォーマンス特性を、並列処理の観点から詳細に分析できるようになりました。特に、TLSハンドシェイクのオーバーヘッド、暗号化/復号化のコスト、およびそれらが多数の同時接続にどのように影響するかを評価するための基盤が提供されます。

関連リンク

参考にした情報源リンク

  • 上記のGo言語公式ドキュメント
  • TLS (Transport Layer Security) の一般的な知識
  • HTTPプロトコルの一般的な知識
  • Go言語のベンチマークの書き方に関する一般的な情報
  • コミットメッセージと変更されたコードの内容
  • Go CL 110080045: https://go.dev/cl/110080045 (これは元コミット内容に記載されているリンクであり、変更の詳細を確認するために参照しました。)
  • GitHubのgolang/goリポジトリ: https://github.com/golang/go
  • Go言語のテストとベンチマークに関する一般的なチュートリアルや記事