Go 博客
介绍 HTTP Tracing
引言
在 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 的七年
上一篇文章: 使用子测试和子基准测试
博客索引