go進行http請求偶發(fā)EOF問題分析
簡介
go使用連接池進行http請求,一般都能請求成功,但偶然會出現請求失敗返回EOF錯誤的情況;類似java的org.apache.http.NoHttpResponseException
分析
客戶端通過keep alive機制保障性能,簡單理解就是復用tcp五元會話,用于進行多次http請求;但如果服務端的空閑?;顣r間是10s,在第一次請求完的10s進行了第二次請求,此時客戶端認為連接仍然有效繼續(xù)發(fā)起請求,但服務端發(fā)出了FIN報文不再對此連接進行響應,從而導致客戶端請求失敗并出現EOF錯誤。
偶發(fā)就是因為兩個時間要恰好碰到一起才可能觸發(fā)這個問題
- 服務器發(fā)送了FIN報文,但是客戶端還沒有收到,但是客戶端已經發(fā)送了請求數據包
- 如果在服務器超時前發(fā)起了請求,那連接此時還可用,正常
- 如果在服務器超時后發(fā)起了請求,那連接已經完成FIN關閉流程,請求會觸發(fā)新的會話,正常
解決方式:
- 在出現EOF的時候,進行重試,此時會觸發(fā)新的五元組連接進行請求(推薦)
- 設置客戶端的空閑?;顣r間小于服務端的空閑?;顣r間
- IdleConnTimeout 此時客戶端會在超時時主動向服務端發(fā)送RST進行連接重置
代碼
package main import ( "bytes" "crypto/tls" "encoding/json" "errors" "fmt" "io" "io/ioutil" "net/http" "time" ) func main() { // 創(chuàng)建自定義的 Transport,設置連接池參數 tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, // 忽略 TLS 證書驗證 }, MaxIdleConns: 1, // 限制最大空閑連接數為1 MaxIdleConnsPerHost: 1, // 限制每個host最大空閑連接數為1 IdleConnTimeout: 20 * time.Second, // 本地空閑連接超時設置為20s DisableKeepAlives: false, // 啟用 keep-alive MaxConnsPerHost: 1, // 限制每個host的最大連接數為1,強制復用連接 ForceAttemptHTTP2: false, // 禁用 HTTP/2 } // 創(chuàng)建 HTTP 客戶端 client := &http.Client{ Transport: tr, Timeout: 5 * time.Second, // 設置請求超時時間 } // 準備請求參數 url := "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977" cookie := "session-id=f416b188c91bc72a06853b362d5cb7b3a6b68a43" // 準備請求體數據 requestBody := map[string]string{ "sys_name": "N-GUARD", } // 將 map 轉換為 JSON jsonBody, err := json.Marshal(requestBody) if err != nil { fmt.Printf("JSON 編碼失敗: %v\n", err) } // 循環(huán)發(fā)送請求,模擬使用已關閉的連接 for i := 0; i < 5; i++ { // 只測試兩次請求即可 // 每次請求都創(chuàng)建新的 bytes.Buffer,確保 Body 可以重復讀取 bodyReader := bytes.NewBuffer(jsonBody) req, err := http.NewRequest("PUT", url, bodyReader) if err != nil { fmt.Printf("創(chuàng)建請求失敗: %v\n", err) continue } // 設置 Content-Length req.ContentLength = int64(len(jsonBody)) // 設置請求頭 req.Header.Set("Cookie", cookie) req.Header.Set("Content-Type", "application/json") fmt.Printf("發(fā)送第 %d 個請求...\n", i+1) // 發(fā)送請求 resp, err := client.Do(req) if err != nil { fmt.Printf("請求失敗: %v\n", err) if errors.Is(err, io.EOF) { fmt.Printf("連接不再可用: 重試:新的五元重新發(fā)起連接\n") bodyReader := bytes.NewBuffer(jsonBody) reqretry, err := http.NewRequest("PUT", url, bodyReader) if err != nil { fmt.Printf("創(chuàng)建請求失敗: %v\n", err) continue } // 設置 Content-Length reqretry.ContentLength = int64(len(jsonBody)) // 設置請求頭 reqretry.Header.Set("Cookie", cookie) reqretry.Header.Set("Content-Type", "application/json") resp, err = client.Do(reqretry) if err != nil { fmt.Printf("err:\n", err) continue } } else { continue } } // 讀取響應 body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("讀取響應失敗: %v\n", err) } resp.Body.Close() fmt.Printf("請求 %d - 狀態(tài)碼: %d, 響應: %s\n", i+1, resp.StatusCode, string(body)) fmt.Println("等待10秒后發(fā)送第二個請求...") time.Sleep(10 * time.Second) // 等待10秒,此時服務端已經關閉連接(10s) time.Sleep(500 * time.Millisecond) } }
運行
[xiaofeng@localhost httpkeepalive]$ go run main.go
發(fā)送第 1 個請求...
請求 1 - 狀態(tài)碼: 200, 響應: {"code":0,"result":"0","message":"成功"}
等待10秒后發(fā)送第二個請求...
發(fā)送第 2 個請求...
請求 2 - 狀態(tài)碼: 200, 響應: {"code":0,"result":"0","message":"成功"}
等待10秒后發(fā)送第二個請求...
發(fā)送第 3 個請求...
請求失敗: Put "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977": EOF
連接不再可用: 重試:新的五元重新發(fā)起連接
請求 3 - 狀態(tài)碼: 200, 響應: {"code":0,"result":"0","message":"成功"}
等待10秒后發(fā)送第二個請求...
發(fā)送第 4 個請求...
請求失敗: Put "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977": EOF
連接不再可用: 重試:新的五元重新發(fā)起連接
請求 4 - 狀態(tài)碼: 200, 響應: {"code":0,"result":"0","message":"成功"}
等待10秒后發(fā)送第二個請求...
發(fā)送第 5 個請求...
請求 5 - 狀態(tài)碼: 200, 響應: {"code":0,"result":"0","message":"成功"}
等待10秒后發(fā)送第二個請求...
報文
到此這篇關于go進行http請求偶發(fā)EOF問題分析的文章就介紹到這了,更多相關go http請求偶發(fā)EOF內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang使用crypto/ed25519實現數字簽名和驗證
本文將深入探討如何在?Golang?中使用?crypto/ed25519?進行數字簽名和驗證,我們將從基本原理開始,逐步引導讀者了解生成密鑰對、進行數字簽名,以及驗證簽名的具體過程,希望對大家有所幫助2024-02-02