最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言HTTP請求流式寫入body的示例代碼

 更新時間:2020年06月03日 09:28:11   作者:mokeyWie  
這篇文章主要介紹了Go語言HTTP請求流式寫入body,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

背景

最近在開發(fā)一個功能時,需要通過 http 協(xié)議上報大量的日志內(nèi)容,但是在 Go 標準庫里的 http client 的 API 是這樣的:

http.NewRequest(method, url string, body io.Reader)

body 是通過 io.Reader 接口來傳遞,并沒有暴露一個 io.Writer 接口來提供寫入的辦法,先來看看正常情況下怎么寫入一個 body ,示例:

需要先把要寫

buf := bytes.NewBuffer([]byte("hello"))
http.Post("localhost:8099/report","text/pain",buf)

入的數(shù)據(jù)放在 Buffer 中,放內(nèi)存緩存著,但是我需要寫入 大量 的數(shù)據(jù),如果都放內(nèi)存里肯定要 OOM 了,http client 并沒有提供 流式寫入 的方法,我這么大的數(shù)據(jù)量直接用 Buffer 肯定是不行的,最后在 google 了一番之后找到了解決辦法。

使用 io.pipe

調(diào)用 io.pipe() 方法會返回 ReaderWriter 接口實現(xiàn)對象,通過 Writer 寫數(shù)據(jù), Reader 就可以讀到,利用這個特性就可以實現(xiàn)流式的寫入,開一個協(xié)程來寫,然后把 Reader 傳遞到方法中,就可以實現(xiàn) http client body 的流式寫入了。

代碼示例:

pr, rw := io.Pipe()
// 開協(xié)程寫入大量數(shù)據(jù)
go func(){
 for i := 0; i < 100000; i++ {
 rw.Write([]byte(fmt.Sprintf("line:%d\r\n", i)))
 }
 rw.Close()
}()
// 傳遞Reader
http.Post("localhost:8099/report","text/pain",buf)

源碼閱讀 目的

了解 go 中 http client 對于 body 的傳輸是如何處理的。

開始

在構(gòu)建 Request 的時候,會斷言 body 參數(shù)的類型,當(dāng)類型為 *bytes.Buffer 、 *bytes.Reader 、 *strings.Reader 的時候,可以直接通過 Len() 方法取出長度,用于 Content-Length 請求頭,相關(guān)代碼net/http/request.go#L872-L914

if body != nil {
 switch v := body.(type) {
 case *bytes.Buffer:
 req.ContentLength = int64(v.Len())
 buf := v.Bytes()
 req.GetBody = func() (io.ReadCloser, error) {
  r := bytes.NewReader(buf)
  return ioutil.NopCloser(r), nil
 }
 case *bytes.Reader:
 req.ContentLength = int64(v.Len())
 snapshot := *v
 req.GetBody = func() (io.ReadCloser, error) {
  r := snapshot
  return ioutil.NopCloser(&r), nil
 }
 case *strings.Reader:
 req.ContentLength = int64(v.Len())
 snapshot := *v
 req.GetBody = func() (io.ReadCloser, error) {
  r := snapshot
  return ioutil.NopCloser(&r), nil
 }
 default:
 }
 if req.GetBody != nil && req.ContentLength == 0 {
 req.Body = NoBody
 req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil }
 }
}

在鏈接建立的時候,會通過 body 和上一步中得到的 ContentLength 來進行判斷,如果 body!=nil 并且 ContentLength==0 時,可能就會啟用 Chunked 編碼進行傳輸,相關(guān)代碼 net/http/transfer.go#L82-L96  :

case *Request:
 if rr.ContentLength != 0 && rr.Body == nil {
 return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength)
 }
 t.Method = valueOrDefault(rr.Method, "GET")
 t.Close = rr.Close
 t.TransferEncoding = rr.TransferEncoding
 t.Header = rr.Header
 t.Trailer = rr.Trailer
 t.Body = rr.Body
 t.BodyCloser = rr.Body
 // 當(dāng)body為非nil,并且ContentLength==0時,這里返回-1
 t.ContentLength = rr.outgoingLength()
 // TransferEncoding沒有手動設(shè)置,并且請求方法為PUT、POST、PATCH時,會啟用chunked編碼傳輸
 if t.ContentLength < 0 && len(t.TransferEncoding) == 0 && t.shouldSendChunkedRequestBody() {
 t.TransferEncoding = []string{"chunked"}
 }

驗證(一)

按照對源碼的理解,可以得知在使用 io.pipe() 方法進行流式傳輸時,會使用 chunked 編碼進行傳輸,通過以下代碼進行驗證:

服務(wù)端

func main(){
 http.HandleFunc("/report", func(writer http.ResponseWriter, request *http.Request) {

 })
 http.ListenAndServe(":8099", nil)
}

客戶端

func main(){
 pr, rw := io.Pipe()
 go func(){
 for i := 0; i < 100; i++ {
  rw.Write([]byte(fmt.Sprintf("line:%d\r\n", i)))
 }
 rw.Close()
 }()
 http.Post("localhost:8099/report","text/pain",buf)
}

先運行服務(wù)端,然后運行客戶端,并且使用 WireShake 進行抓包分析,結(jié)果如下:

可以看到和預(yù)想的結(jié)果一樣。

驗證(二)

在數(shù)據(jù)量大的時候 chunked 編碼會增加額外的開銷,包括編解碼和額外的報文開銷,能不能不用 chunked 編碼來進行 流式傳輸 呢?通過源碼可以得知,當(dāng) ContentLength 不為 0 時,如果能預(yù)先計算出待傳輸?shù)?body size ,是不是就能避免 chunked 編碼呢?思路就到這,接著就是寫代碼驗證:

服務(wù)端

func main(){
 http.HandleFunc("/report", func(writer http.ResponseWriter, request *http.Request) {

 })
 http.ListenAndServe(":8099", nil)
}

客戶端

count := 100
line := []byte("line\r\n")
pr, rw := io.Pipe()
go func() {
 for i := 0; i < count; i++ {
 rw.Write(line)
 }
 rw.Close()
}()
// 構(gòu)造request對象
request, err := http.NewRequest("POST", "http://localhost:8099/report", pr)
if err != nil {
 log.Fatal(err)
}
// 提前計算出ContentLength
request.ContentLength = int64(len(line) * count)
// 發(fā)起請求
http.DefaultClient.Do(request)

抓包結(jié)果:

可以看到確實直接使用的 Content-Length 進行傳輸,沒有進行 chunked 編碼了。

總結(jié)

本文的目的主要是記錄 go 語言中 http client 如何進行流式的寫入,并通過閱讀源碼了解 http client 內(nèi)部對 body 的寫入是如何進行處理的,通過兩個驗證可以得知,如果能提前計算出 ContentLength 并且對性能要求比較苛刻的情況下,可以通過手動設(shè)置 ContentLength 來優(yōu)化性能。

到此這篇關(guān)于Go語言HTTP請求流式寫入body的文章就介紹到這了,更多相關(guān)Go語言HTTP請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go 實現(xiàn)基于Token 的登錄流程深度分析

    Go 實現(xiàn)基于Token 的登錄流程深度分析

    Token 認證機制的核心思想是,服務(wù)端在用戶登錄時生成一個 Token,客戶端在后續(xù)的請求中攜帶這個 Token,服務(wù)端通過驗證 Token 的有效性來確認用戶的身份,本文將帶你深入探索基于 Token 的登錄流程,這是一種更為靈活且適用于現(xiàn)代應(yīng)用架構(gòu)的認證方式
    2024-03-03
  • 使用gin框架搭建簡易服務(wù)的實現(xiàn)方法

    使用gin框架搭建簡易服務(wù)的實現(xiàn)方法

    go語言web框架挺多的,本文就介紹了一下如何使用gin框架搭建簡易服務(wù)的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Golang中urlencode與urldecode編碼解碼詳解

    Golang中urlencode與urldecode編碼解碼詳解

    這篇文章主要給大家介紹了關(guān)于Golang中urlencode與urldecode編碼解碼的相關(guān)資料,在Go語言中轉(zhuǎn)碼操作非常方便,可以使用內(nèi)置的encoding包來快速完成轉(zhuǎn)碼操作,Go語言中的encoding包提供了許多常用的編碼解碼方式,需要的朋友可以參考下
    2023-09-09
  • 詳解Golang中日志庫glog的使用

    詳解Golang中日志庫glog的使用

    golang/glog?是?C++?版本?google/glog?的?Go?版本實現(xiàn),基本實現(xiàn)了原生?glog?的日志格式,下面大家就跟隨小編一起了解一下glog的具體使用吧
    2023-09-09
  • Golang中堆排序的實現(xiàn)

    Golang中堆排序的實現(xiàn)

    堆是一棵基于數(shù)組實現(xiàn)的特殊的完全二叉樹,本文主要介紹了Golang中堆排序的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • golang中連接mysql數(shù)據(jù)庫

    golang中連接mysql數(shù)據(jù)庫

    這篇文章主要介紹了golang中連接mysql數(shù)據(jù)庫的步驟,幫助大家更好的理解和學(xué)習(xí)go語言,感興趣的朋友可以了解下
    2020-12-12
  • golang程序使用alpine編譯出最小arm鏡像實現(xiàn)

    golang程序使用alpine編譯出最小arm鏡像實現(xiàn)

    這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • go實現(xiàn)限流功能示例

    go實現(xiàn)限流功能示例

    這篇文章主要為大家介紹了go實現(xiàn)限流功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • golang使用tail實現(xiàn)追蹤文件變更

    golang使用tail實現(xiàn)追蹤文件變更

    這篇文章主要為大家介紹了golang如何借助 github.com/hpcloud/tail ,實現(xiàn)實時追蹤文件變更,達到類似shell命令tail -f的效果,感興趣的小伙伴可以了解一下
    2023-08-08
  • Go使用Pipeline實現(xiàn)一個簡潔而高效的數(shù)據(jù)處理流水線

    Go使用Pipeline實現(xiàn)一個簡潔而高效的數(shù)據(jù)處理流水線

    在并發(fā)編程中,流水線Pipeline是一種常見的設(shè)計模式,它將一個復(fù)雜任務(wù)拆解為多個獨立步驟,由多個協(xié)程并行處理并通過通道傳遞數(shù)據(jù),Go語言天生支持這種模型,能顯著提高數(shù)據(jù)處理的性能和可讀性,本文將給大家介紹如何使用Go實現(xiàn)一個簡潔而高效的數(shù)據(jù)處理流水線
    2025-08-08

最新評論

冕宁县| 双城市| 盐津县| 中西区| 东港市| 柞水县| 铁力市| 原阳县| 武义县| 德庆县| 肃南| 新巴尔虎右旗| 突泉县| 平顺县| 新龙县| 镇远县| 沿河| 民和| 德安县| 湖口县| 饶河县| 武陟县| 伽师县| 新蔡县| 广东省| 门源| 马龙县| 大余县| 衡水市| 克拉玛依市| 安平县| 蒙自县| 安远县| 桐庐县| 灵丘县| 穆棱市| 红桥区| 大名县| 兰考县| 延长县| 盱眙县|