跳到主要内容

HTTP 服务进阶

问题

Go HTTP 服务在生产环境中需要注意哪些问题?如何配置超时和连接池?

答案

生产级 Server 配置

server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second, // 读取请求的超时
ReadHeaderTimeout: 5 * time.Second, // 读取请求头的超时
WriteTimeout: 15 * time.Second, // 写入响应的超时
IdleTimeout: 120 * time.Second, // Keep-Alive 空闲超时
MaxHeaderBytes: 1 << 20, // 请求头最大 1MB
}
超时必须设置

不设置超时 = 慢连接或恶意客户端可以无限期占用连接资源,最终耗尽服务器资源。

生产级 Client 配置

transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // 建立连接超时
KeepAlive: 30 * time.Second, // Keep-Alive 探活间隔
}).DialContext,
MaxIdleConns: 100, // 全局最大空闲连接
MaxIdleConnsPerHost: 10, // 每个 host 最大空闲连接
MaxConnsPerHost: 100, // 每个 host 最大连接数
IdleConnTimeout: 90 * time.Second,// 空闲连接超时
TLSHandshakeTimeout: 5 * time.Second, // TLS 握手超时
ResponseHeaderTimeout: 10 * time.Second, // 等待响应头超时
}

client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second, // 整体请求超时
}

HTTP/2

Go 的 net/http 默认支持 HTTP/2(当使用 TLS 时自动启用):

// HTTPS 自动启用 HTTP/2
server.ListenAndServeTLS("cert.pem", "key.pem")

// 明文 HTTP/2(h2c)需要手动配置
import "golang.org/x/net/http2"
import "golang.org/x/net/http2/h2c"

h2s := &http2.Server{}
handler := h2c.NewHandler(mux, h2s)

常见面试问题

Q1: http.Client 为什么要复用?

答案http.Client 内部维护着 TCP 连接池(Transport)。每次创建新 Client 会创建新连接池,无法复用 TCP 连接(每次请求都要 TCP 握手 + TLS 握手),严重影响性能。

正确做法:创建一个全局 Client 并复用。

Q2: resp.Body 不关闭会怎样?

答案:TCP 连接不会归还连接池,下次请求需要重新建立连接。连接池中的空闲连接会逐渐耗尽,最终每次请求都要重新三次握手。而且不读完 Body 也不行——必须 io.Copy(io.Discard, resp.Body) 读完才能复用连接。

相关链接