Go 部落格

介紹 HTTP Tracing

Jaana Burcu Dogan
2016年10月4日

引言

在 Go 1.7 中,我們引入了 HTTP Tracing,這是一種在 HTTP 客戶端請求的整個生命週期中收集細粒度資訊的功能。HTTP Tracing 的支援由 net/http/httptrace 包提供。收集到的資訊可用於除錯延遲問題、服務監控、編寫自適應系統等。

HTTP 事件

httptrace 包提供了許多鉤子,可在 HTTP 往返過程中收集各種事件的資訊。這些事件包括:

  • 連線建立
  • 連線重用
  • DNS 查詢
  • 將請求寫入網路
  • 讀取響應

Tracing 事件

您可以透過將包含鉤子函式的 *httptrace.ClientTrace 放入請求的 context.Context 中來啟用 HTTP Tracing。各種 http.RoundTripper 實現透過查詢上下文中的 *httptrace.ClientTrace 並呼叫相關的鉤子函式來報告內部事件。

Tracing 的範圍限定在請求的上下文中,使用者應該在開始請求之前將 *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 進行 Tracing

Tracing 機制旨在跟蹤單個 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 請求的 tracing。

如果您是自定義 http.RoundTripper 實現的作者,可以透過檢查請求上下文是否包含 *httptest.ClientTrace 並隨著事件的發生呼叫相關的鉤子來支援 tracing。

結論

對於那些對除錯 HTTP 請求延遲感興趣併為出站流量編寫網路除錯工具的人來說,HTTP Tracing 是 Go 的一項有價值的補充。透過啟用此新功能,我們希望看到社群開發出 HTTP 除錯、基準測試和視覺化工具,例如 httpstat

下一篇文章: Go 的七年
上一篇文章: 使用子測試和子基準測試
部落格索引