日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
GoBIO/NIO探討:Gin框架中如何處理HTTP請(qǐng)求

最近看到字節(jié)跳動(dòng)開源了Go語言的Hertz,聲稱使用了 Non-blocking IO 網(wǎng)絡(luò)庫 Netpoll,所以性能非常強(qiáng)大,并給出了Echo的性能測(cè)試數(shù)據(jù)。

公司主營業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出鄂爾多斯免費(fèi)做網(wǎng)站回饋大家。

性能測(cè)試

相對(duì)于目前業(yè)界流行的 gin 框架,QPS提升超過100%,而且單次請(qǐng)求的數(shù)據(jù)包越大,性能提升越明顯。

熟悉Java的朋友看到 NIO 這個(gè)詞,應(yīng)該會(huì)有莫名的親切感。值得慶幸的是,現(xiàn)在終于有人用Go翻譯一波netty了。

在這之前,為了更好地理解Blocking IO,我們從 Web 框架入手,看 Go 內(nèi)置的 net/http 是如何工作的。

下面這段代碼展示了Gin框架中如何定義一個(gè)路由,并啟動(dòng)Server。代碼來自于 Github:gin-gonic/gin,我們從這段代碼入手,了解 http 的運(yùn)行機(jī)制。

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.Run() // 監(jiān)聽8080端口
}

在這段代碼中,r 是一個(gè) *gin.Engine 對(duì)象,它實(shí)現(xiàn)了 net/http下的Handler 接口:

type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

第二步是設(shè)置路由和對(duì)應(yīng)的處理函數(shù),*gin.Engine 對(duì)象嵌入了 struct RouterGroup,實(shí)現(xiàn)路由功能。Web開發(fā)中,這是所有業(yè)務(wù)邏輯的入口。

第三步是運(yùn)行服務(wù),基本流程是三個(gè)tcp系統(tǒng)調(diào)用 socket, bind, listen,然后在 for 循環(huán)里調(diào)用 accept 接收新的tcp連接(更多細(xì)節(jié)參考Unix網(wǎng)絡(luò)編程卷一第四章)。

// 來源于Go源碼 net/http/server.go struct Server
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}

func (srv *Server) Serve(l net.Listener) error {
// ... 省略一部分代碼

origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()

if err := srv.setupHTTP2_Serve(); err != nil {
return err
}

// ... 省略一部分代碼

ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
// ... 省略一部分代碼
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}

到這里,HTTP還沒有登場(chǎng),因?yàn)镠TTP是應(yīng)用層協(xié)議,而TCP是傳輸層協(xié)議。我們拿到 tcp connection 以后,HTTP協(xié)議體現(xiàn)在對(duì)數(shù)據(jù)包的處理上。

HTTP/1 仍然是一個(gè)超文本協(xié)議,HTTP/2 是一個(gè)基于幀的二進(jìn)制協(xié)議,并且支持了server端主動(dòng)推送。由于 HTTP/1 仍然是主流,我們這里先說 HTTP/1,它的報(bào)文分三部分:

  1. 起始行
  2. Headers 消息頭,以一個(gè)空行結(jié)束
  3. Body 消息體

應(yīng)用程序可以方便地解析HTTP 請(qǐng)求和響應(yīng)。HTTP/1.1 已經(jīng)支持復(fù)用TCP連接,也就是說多個(gè)HTTP請(qǐng)求可以在同一個(gè)tcp conn上傳輸,那么client端如何把request和response 對(duì)應(yīng)起來呢?會(huì)有什么問題?HTTP/2中又是怎么做的?這里先挖個(gè)坑。

繼續(xù)看 net/http 的代碼,了解HTTP 如何在tcp conn之上工作的。主要邏輯如下:

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
var inFlightResponse *response
defer func() {... // cleanup

if tlsConn, ok := c.rwc.(*tls.Conn); ok {
// ... 省略部分代碼
}

// HTTP/1.x from here on.
ctx, cancelCtx := context.WithCancel(ctx)
c.cancelCtx = cancelCtx
defer cancelCtx()

c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

for {
w, err := c.readRequest(ctx)
// ... 省略部分代碼
serverHandler{c.server}.ServeHTTP(w, w.req)
// ... 省略一部分代碼
w.finishRequest()
// ... 省略一部分代碼
}

這段代碼的前半部分是設(shè)置cleanup邏輯、初始化conn相關(guān)的變量,后半部分是在 for 循環(huán)里處理 HTTP 請(qǐng)求,體現(xiàn)為:

  1. 讀取 tcp conn上的數(shù)據(jù),并解析&組裝成一個(gè)請(qǐng)求,存儲(chǔ)到一個(gè)http.response對(duì)象里。
  2. 通過 ServeHTTP 調(diào)用業(yè)務(wù)邏輯(路由轉(zhuǎn)發(fā)到特定處理函數(shù)),獲取響應(yīng)。
  3. 把響應(yīng)報(bào)文寫入 tcp conn。

關(guān)于Gin框架和net/http 的邏輯到這里就介紹完了,大的流程參考下面這個(gè)思維導(dǎo)圖:

為什么 Blocking IO

當(dāng)我們聊到 Blocking IO 和 Non-blocking IO,通常是指一個(gè)線程調(diào)用 read 或 write 時(shí),是否被阻塞:

  • BIO: 線程被阻塞直到讀到數(shù)據(jù)或?qū)懭胪瓿伞?/li>
  • NIO: 線程不被阻塞,可以去做其他事情,但是有數(shù)據(jù)到來或者寫入完成時(shí),線程會(huì)接收到通知。

這里我們限定到網(wǎng)絡(luò)IO的情況,Go net/http 里BIO體現(xiàn)在兩個(gè)地方:

  • case 1: listener 在for循環(huán)里等待接收新的tcp conn。
  • case 2: conn 等待讀取新的 request。
// case 1
for {
rw, err := l.Accept()
// ... 省略一部分代碼
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}

// case 2
for {
w, err := c.readRequest(ctx)
// ... 省略部分代碼
serverHandler{c.server}.ServeHTTP(w, w.req)
// ... 省略一部分代碼
w.finishRequest()
// ... 省略一部分代碼
}

如果把 goroutine 當(dāng)成操作系統(tǒng)線程,我們可以把這種模式當(dāng)作BIO。由于在網(wǎng)絡(luò)IO上 Go語言對(duì) goroutine 的特殊實(shí)現(xiàn),在細(xì)節(jié)上可能會(huì)有點(diǎn)爭議,有興趣的同學(xué)可以自己研究下。

要做到 NIO,典型的方式是通過 reactor 模式,替換上面的兩個(gè)for循環(huán)。在后面的文章中,我們介紹 hertz 框架的時(shí)候,會(huì)詳細(xì)聊一下這個(gè)方案。


當(dāng)前名稱:GoBIO/NIO探討:Gin框架中如何處理HTTP請(qǐng)求
本文鏈接:http://m.5511xx.com/article/dpshgee.html