Goブログ

HTTPトレーシングの紹介

Jaana Burcu Dogan
2016年10月4日

はじめに

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

HTTPイベント

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

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

トレースイベント

フック関数を含む*httptrace.ClientTraceをリクエストのcontext.Contextに入れることで、HTTPトレーシングを有効にできます。さまざまな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年間
前の記事:サブテストとサブベンチマークの使用
ブログインデックス