深入理解 Golang HTTP Timeout
背景
前段時間脓斩,線上服務(wù)器因?yàn)椴糠治⒎?wù)提供的 HTTP API 響應(yīng)慢肺素,而我們沒有用正確的姿勢來處理 HTTP 超時(當(dāng)然,還有少量 RPC 超時), 同時我們也沒有服務(wù)降級策略和容災(zāi)機(jī)制,導(dǎo)致服務(wù)相繼掛掉??。服務(wù)降級和容災(zāi)需要一段時間的架構(gòu)改造,但是以正確的姿勢使用 HTTP 超時確是馬上可以習(xí)得的典挑。
超時的本質(zhì)
所有的 Timeout 都構(gòu)建于 Golang 提供的 Set[Read|Write]Deadline 原語之上。
服務(wù)器超時
ReadTimout 包括了TCP 消耗的時間啦吧,可以一定程度預(yù)防慢客戶端和意外斷開的客戶端占用文件描述符
對于 https請求您觉,ReadTimeout 包括了 TLS 握手的時間;WriteTimeout 包括了 TLS握手授滓、讀取 Header 的時間(虛線部分), 而 http 請求只包括讀取 body 和寫 response 的時間琳水。
此外,http.ListenAndServe, http.ListenAndServeTLS and http.Serve 等方法都沒有設(shè)置超時般堆,且無法設(shè)置超時在孝。因此不適合直接用來提供公網(wǎng)服務(wù)。正確的姿勢是:
package main
import (
"net/http"
"time"
)
func main() {
server := &http.Server{
Addr: ":8081",
ReadTimeout: 3 * time.Second,
WriteTimeout: 5 * time.Second,
}
http.HandleFunc("/hi", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
})
server.ListenAndServe()
}
客戶端超時
- http.Client 會自動跟隨重定向(301, 302), 重定向時間也會記入 http.Client.Timeout, 這點(diǎn)一定要注意淮摔。
取消一個 http request 有兩種方式:
Request.Cancel
Context (Golang >= 1.7.0)
后一種因?yàn)榭梢詡鬟f parent context, 因此可以做級聯(lián) cancel, 效果更佳私沮。代碼示例:
ctx, cancel := context.WithCancel(context.TODO()) // or parant context
timer := time.AfterFunc(5*time.Second, func() {
cancel()
})
req, err := http.NewRequest("GET", "http://httpbin.org/range/2048?duration=8&chunk_size=256", nil)
if err != nil {
log.Fatal(err)
}
req = req.WithContext(ctx)
Credits
同步自我的博客深入理解 Golang HTTP Timeout