Golang使用Gin框架實現(xiàn)http分塊傳輸
今天,跟大家聊聊gin框架中是如何實現(xiàn)分片輸出的。主要分以下4點:
- 分片輸出的效果圖
- gin實現(xiàn)分片傳輸代碼
- http分片傳輸?shù)幕A:transfer-encoding
- gin實現(xiàn)分片傳輸原理
效果圖
首先看下分片輸出的效果圖:
gin分片傳輸實現(xiàn)代碼
上面的效果圖中,網(wǎng)頁中的內(nèi)容不斷的輸出。在gin中是主要是利用了Flush函數(shù)實現(xiàn)的。如下代碼:
package main import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/test_stream", func(c *gin.Context) { w := c.Writer header := w.Header() header.Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) w.Write([]byte(` <html> <body> `)) w.(http.Flusher).Flush() // 這里對每次循環(huán)輸出都進行Flush刷新輸出 for i := 0; i < 10; i++ { w.Write([]byte(fmt.Sprintf(` <h3>%d</h3> `, i))) //w.Flush() w.(http.Flusher).Flush() time.Sleep(time.Duration(1)*time.Second) } w.Write([]byte(` </body> </html> `)) w.(http.Flusher).Flush() }) r.Run("127.0.0.1:8080") }
這里主要就是利用了第22行中的w.(http.Flusher).Flush()。 這里的Flush本質(zhì)上就是將Write的內(nèi)容立即輸出到客戶端的意思。
那么,為什么通過Flush就能實現(xiàn)上述效果呢?
分塊傳輸?shù)幕A:http的 transfer-encoding:chunked 協(xié)議
分塊傳輸?shù)幕A就是http中的transfer-encoding:chunked協(xié)議。在http響應報文中用頭字段“Transfer-Encoding: chunked”,表示響應中的body不是一次性發(fā)送完畢,而是分成了許多的塊(chunk)逐個發(fā)送,直到發(fā)送完畢。
分塊傳輸?shù)木幋a規(guī)則如下: 1)每個分塊包含兩個部分,<長度頭>和&<數(shù)據(jù)塊> 2) <長度頭>是以 CRLF(回車換行,即\r\n)結尾的一行明文,用 16 進制數(shù)字表示長度 3) <數(shù)據(jù)塊>緊跟在<長度頭>后,最后也用 CRLF 結尾,但數(shù)據(jù)不包含 CRLF 4)最后用一個長度為 0 的塊表示數(shù)據(jù)傳輸結束,即“0\r\n\r\n”。
為什么通過Flush函數(shù)就能實現(xiàn)分塊傳輸
到了本篇的核心部分了,為什么在gin中通過Flush函數(shù)就能實現(xiàn)分塊傳輸了呢?首先,在gin框架中正常的輸出是通過Context.Writer.Write函數(shù)進行輸出的。而Writer是net/http包中的response對象,該response對象包含了本次http的連接對象conn。以下是從Context.Writer對象到conn對象的一個層級關系,如下:
Context.Writer對象指向了response對象,response對象中包含一個緩沖的Writer對象w,w的底層輸出對象時chunkWriter對象cw,cw又指向了本次的http連接對象response.conn。
那么,基于這個層級結構,Context.Writer.Write的寫入過程如下:
我們簡化一些,就是Context.Writer.Write先將內(nèi)容寫入到緩沖區(qū)w中,然后等本次請求邏輯處理完畢,再調(diào)用緩存區(qū)w的Flush功能,將緩沖區(qū)w中的內(nèi)容寫入到cw中,然后調(diào)用cw的flush功能,這時就寫入了http的響應頭Content-Length為寫入數(shù)據(jù)的長度,并且將內(nèi)容通過conn.bufw.flush輸出給客戶端。
簡化一下gin的輸出過程:內(nèi)容先寫入到緩沖區(qū),最后將緩沖區(qū)的內(nèi)容一次性全部輸出給客戶端。
劃重點,Content-Length頭部的輸出是和分塊傳輸?shù)闹饕獏^(qū)別。
接下來再看分塊輸出。
其實現(xiàn)的思想就是通過http的Transfer-Encoding: chunked頭告訴客戶端,服務端的內(nèi)容要分塊傳輸了。然后服務端就將內(nèi)容先寫入緩沖區(qū),然后立即使用Flush函數(shù)將緩沖區(qū)的內(nèi)容輸出到客戶端。這就是一個塊的輸出。然后依次循環(huán)寫入,F(xiàn)lush刷新輸出這個過程。
下圖是gin中分塊傳輸?shù)牧鞒虉D:
在分塊輸出的時候,在response.cw.flush階段,可以判定到該請求還未處理完畢(在net/http包中,本次請求處理完畢才會調(diào)用一個finishRequest的函數(shù)以標識本次請求處理完畢),所以會自動寫入一個http的頭信息: Transfer-Encoding: chunked。當客戶端收到該響應時,檢測到header中的chunked,就表示本次響應還未結束,會繼續(xù)接收后續(xù)的響應內(nèi)容。
簡化一下gin的分塊傳輸流程如下:
總結
當輸出內(nèi)容太大時,就可以使用分塊傳輸?shù)姆绞健7謮K傳輸是基于http的Transfer-Encoding: chunked協(xié)議進行的。當客戶端接收到該響應頭時,就知道服務端的內(nèi)容還沒有傳輸完,不能關閉本次http連接。另一方面,gin框架通過Flush函數(shù)將緩沖區(qū)的內(nèi)容及時輸出來實現(xiàn)分塊傳輸。
到此這篇關于Golang使用Gin框架實現(xiàn)http分塊傳輸?shù)奈恼戮徒榻B到這了,更多相關Golang http分塊傳輸內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang程序使用alpine編譯出最小arm鏡像實現(xiàn)
這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12golang進程內(nèi)存控制避免docker內(nèi)oom
這篇文章主要為大家介紹了golang進程內(nèi)存控制避免docker內(nèi)oom示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10