有关[Http持久连接]的一切,卷给你看
上文中我的結論是: ?HTTP Keep-Alive 是在應用層對TCP連接進行滑動續約復用, 如果客戶端/服務器穩定續約,就成了名副其實的長連接。
目前所有的Http網絡庫都默認開啟了HTTP Keep-Alive,今天我們從底層TCP連接和排障角度撕碎HTTP持久連接。
“我只是一個寫web程序的猿,我為什么要知道這么多😂😂😂”。
使用go語言倒騰一個httpServer/httpClient,粗略聊一聊go的使用風格。
使用go語言net/http包快速搭建httpserver,注入用于記錄請求日志的Handler
package?mainimport?("fmt""log""net/http" )// IndexHandler記錄請求的基本信息:?請關注r.RemoteAddr func?Index(w?http.ResponseWriter,?r?*http.Request)?{fmt.Println("receive?a?request?from:",?r.RemoteAddr,?r.Header)w.Write([]byte("ok")) }//?net/http?默認開啟持久連接 func?main()?{?fmt.Printf("Starting?server?at?port?8081\n")if?err?:=?http.ListenAndServe(":8081",?http.HandlerFunc(Index));?err?!=?nil?{log.Fatal(err)} }ListenAndServe創建了默認的httpServer服務器,go通過首字母大小寫來控制訪問權限,如果首字母大寫,則可以被外部包訪問, 類比C#全局函數、靜態函數。
net/http服務器默認開啟了Keep-Alive, 由Server的私有變量disableKeepAlives體現。
使用者也可以手動關閉Keep-Alive, SetKeepAlivesEnabled()會修改私有變量disableKeepAlives的值
s?:=?&http.Server{Addr:???????????":8081",Handler:?http.HandlerFunc(Index),ReadTimeout:????10?*?time.Second,WriteTimeout:???10?*?time.Second,MaxHeaderBytes:?1?<<?20,}s.SetKeepAlivesEnabled(true)if?err?:=?s.ListenAndServe();?err?!=?nil?{log.Fatal(err)}以上也是go語言包的基本制作/使用風格。
請注意我在httpserver插入了IndexHander,記錄httpclient的基本信息。
這里有個知識點:如果httpclient建立新的TCP連接,系統會按照一定規則給你分配隨機端口。
啟動服務器程序,瀏覽器訪問localhost:8081,
服務器會收到如下日志, 圖中紅圈處表明瀏覽器使用了系統隨機的固定端口建立tcp連接。
使用net/http編寫客戶端:間隔1s向服務器發起HTTP請求
package?mainimport?("fmt""io/ioutil""log""net/http""time" )func?main()?{client?:=?&http.Client{Timeout:?10?*?time.Second,}for?{requestWithClose(client)time.Sleep(time.Second?*?1)} }func?requestWithClose(client?*http.Client)?{resp,?err?:=?client.Get("http://127.0.0.1:8081")if?err?!=?nil?{fmt.Printf("error?occurred?while?fetching?page,?error:?%s",?err.Error())return}defer?resp.Body.Close()c,?err?:=?ioutil.ReadAll(resp.Body)if?err?!=?nil?{log.Fatalf("Couldn't?parse?response?body.?%+v",?err)}fmt.Println(string(c)) }服務器收到的請求日志如下:
圖中紅框顯示httpclient使用固定端口61799發起了http請求,客戶端/服務器維持了HTTP Keep-alive。
使用netstat -an | grep 127.0.0.1:8081可圍觀系統針對特定ip的TCP連接:客戶端系統中針對 服務端也只建立了一個tcp連接,tcp連接的端口是61799,與上文呼應。
使用Wireshark查看localhost網卡發生的tcp連接
可以看到每次http請求/響應之前均沒有tcp三次握手
tcp每次發包后,對端需要回ACK確認包
反面教材-高能預警
go的net/http明確提出:
If the Body is not both read to EOF and closed, the Client's underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent "keep-alive" request.
也就是說:httpclient客戶端在每次請求結束后,如果不讀完body或者沒有關閉body, 可能會導致Keep-alive失效,也會導致goroutine泄露。
//??下面的代碼沒有讀完body,導致Keep-alive失效 func?requestWithClose(client?*http.Client)?{resp,?err?:=?client.Get("http://127.0.0.1:8081")if?err?!=?nil?{fmt.Printf("error?occurred?while?fetching?page,?error:?%s",?err.Error())return}defer?resp.Body.Close()//_,?err?=?ioutil.ReadAll(resp.Body)fmt.Println("ok") }此次服務端日志如下:上圖紅框顯示客戶端持續使用新的隨機端口建立了TCP連接。
查看客戶端系統建立的tcp連接:
Wireshark抓包結果:圖中紅框顯示每次HTTP請求/響應 前后均發生了三次握手、四次揮手。
全文梳理
目前已知的httpclient、httpServer均默認開啟keep-alive
禁用keep-alive或者keep-alive失效,會導致特定場景客戶端頻繁建立tcp連接,?可通過?netstat?-an?|?grep?{ip}?查看客戶機上建立的tcp連接
Wireshark抓包, 明確keep-alive和非Keep-alive的抓包效果
●HTTP1.1 Keep-Alive到底算不算長連接?
●寶藏好物gRPCurl
●SignalR 開發到生產部署閉坑指南
●SignalR在React/Go技術棧的實踐
●我是狀態機, 一顆永遠騷動的機器引擎
●大揭秘| 我司項目組Gitlab Flow && DevOps流程
●你怕是對MD5算法有誤解
點個在看你最好看
僅代表此刻認知,文章永久更新地址,請移步原文!!
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的有关[Http持久连接]的一切,卷给你看的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: switch类型模式
- 下一篇: 关于async和await的探讨