The Go Blog

HTTPトレースの紹介

ジャーナ・ブルジュ・ドーガン
2016年10月4日

はじめに

Go 1.7では、HTTPクライアントリクエストのライフサイクル全体を通してきめ細かい情報を収集する機能であるHTTPトレースを導入しました。HTTPトレースのサポートは、net/http/httptraceパッケージによって提供されます。収集された情報は、レイテンシの問題のデバッグ、サービス監視、適応システムの作成などに使用できます。

HTTPイベント

httptraceパッケージは、HTTPラウンドトリップ中にさまざまなイベントに関する情報を収集するための多くのフックを提供します。これらのイベントには以下が含まれます。

  • 接続作成
  • 接続再利用
  • DNSルックアップ
  • リクエストをワイヤーに書き込む
  • レスポンスの読み取り

トレースイベント

HTTPトレースを有効にするには、フック関数を含む*httptrace.ClientTraceをリクエストのcontext.Contextに配置します。さまざまなhttp.RoundTripperの実装は、コンテキストの*httptrace.ClientTraceを検索し、関連するフック関数を呼び出すことで内部イベントを報告します。

トレースはリクエストのコンテキストにスコープされ、ユーザーはリクエストを開始する前に*httptrace.ClientTraceをリクエストコンテキストに配置する必要があります。

    req, _ := http.NewRequest("GET", "http://example.com", nil)
    trace := &httptrace.ClientTrace{
        DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
            fmt.Printf("DNS Info: %+v\n", dnsInfo)
        },
        GotConn: func(connInfo httptrace.GotConnInfo) {
            fmt.Printf("Got Conn: %+v\n", connInfo)
        },
    }
    req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
    if _, err := http.DefaultTransport.RoundTrip(req); err != nil {
        log.Fatal(err)
    }

ラウンドトリップ中に、イベントが発生するとhttp.DefaultTransportは各フックを呼び出します。上記のプログラムは、DNSルックアップが完了するとすぐにDNS情報を出力します。同様に、リクエストのホストに接続が確立されると接続情報が出力されます。

http.Clientによるトレース

トレースメカニズムは、単一のhttp.Transport.RoundTripのライフサイクルにおけるイベントをトレースするように設計されています。ただし、クライアントはHTTPリクエストを完了するために複数のラウンドトリップを行う場合があります。たとえば、URLリダイレクトの場合、登録されたフックは、クライアントがHTTPリダイレクトに従う回数だけ呼び出され、複数のリクエストが行われます。ユーザーは、http.Clientレベルでそのようなイベントを認識する責任があります。以下のプログラムは、http.RoundTripperラッパーを使用して現在のリクエストを識別します。

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httptrace"
)

// transport is an http.RoundTripper that keeps track of the in-flight
// request and implements hooks to report HTTP tracing events.
type transport struct {
    current *http.Request
}

// RoundTrip wraps http.DefaultTransport.RoundTrip to keep track
// of the current request.
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
    t.current = req
    return http.DefaultTransport.RoundTrip(req)
}

// GotConn prints whether the connection has been used previously
// for the current request.
func (t *transport) GotConn(info httptrace.GotConnInfo) {
    fmt.Printf("Connection reused for %v? %v\n", t.current.URL, info.Reused)
}

func main() {
    t := &transport{}

    req, _ := http.NewRequest("GET", "https://google.com", nil)
    trace := &httptrace.ClientTrace{
        GotConn: t.GotConn,
    }
    req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))

    client := &http.Client{Transport: t}
    if _, err := client.Do(req); err != nil {
        log.Fatal(err)
    }
}

プログラムはgoogle.comのリダイレクトをwww.google.comに追跡し、以下の結果を出力します。

Connection reused for https://google.com? false
Connection reused for https://www.google.com/? false

net/httpパッケージのTransportは、HTTP/1とHTTP/2の両方のリクエストのトレースをサポートしています。

カスタムhttp.RoundTripper実装の作成者である場合は、リクエストコンテキストで*httptest.ClientTraceをチェックし、イベントが発生したときに適切なフックを呼び出すことでトレースをサポートできます。

まとめ

HTTPトレースは、HTTPリクエストのレイテンシをデバッグしたり、アウトバウンドトラフィックのネットワークデバッグツールを作成したりすることに関心のある人々にとって、Goへの貴重な追加機能です。この新しい機能を有効にすることで、コミュニティからhttpstatのようなHTTPデバッグ、ベンチマーク、視覚化ツールが登場することを期待しています。

次の記事:Goの7年間
前の記事:サブテストとサブベンチマークの使用
ブログインデックス