go net包分析二(net/http)

net/http包也是net包中的咳短,是在net tcp協(xié)議基礎(chǔ)上實(shí)現(xiàn)的http協(xié)議。底層調(diào)用的方法也是基于net dial里面的那幾個(gè)方法砰粹,當(dāng)然不止于這些唧躲,下面大致從http服務(wù)器創(chuàng)建開始說起。

一碱璃、創(chuàng)建http服務(wù)
先簡(jiǎn)單看下代碼:

func main()  {
    http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
        fmt.Println("Hello World.")
        fmt.Fprintf(w, "Hello World.\n")
    })
    err := http.ListenAndServe("0.0.0.0:8899", nil)
    if err != nil {
        fmt.Println("http listen failed.")
    }
}

簡(jiǎn)單幾行代碼就搭建了一個(gè)http服務(wù)弄痹,在瀏覽器通過http://127.0.0.1:8899 訪問一下就可以看到結(jié)果。是不是很簡(jiǎn)單厘贼,只需要兩個(gè)方法界酒,一個(gè)ListenAndServe 方法,它的作用就是啟動(dòng)服務(wù)端監(jiān)聽端口嘴秸,監(jiān)聽8899端口過來的請(qǐng)求毁欣,請(qǐng)求過來以后怎么處理,就要看HandleFunc方法岳掐,這個(gè)是對(duì)請(qǐng)求過來后訪問的路徑綁定處理函數(shù)凭疮。為了簡(jiǎn)單就只有一個(gè)路徑,如果想添加多個(gè)訪問uri串述,就在多加幾個(gè)HandleFunc即可执解。

HandleFunc 里面?zhèn)鬟f了兩個(gè)參數(shù)w http.ResponseWriter, request *http.Request
這兩個(gè)是什么,我們看下它們的介紹

// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(statusCode int)
}

type Request struct {
    Method string
    URL *url.URL
    Proto      string // "HTTP/1.0"
    ProtoMajor int    // 1
    ProtoMinor int    // 0
    Header Header
    Body io.ReadCloser
    GetBody func() (io.ReadCloser, error)
    ContentLength int64
    TransferEncoding []string
    Close bool
    Host string
    Form url.Values
    PostForm url.Values
    MultipartForm *multipart.Form
    Trailer Header
    RemoteAddr string
    RequestURI string
    TLS *tls.ConnectionState
    Cancel <-chan struct{}
    Response *Response
    ctx context.Context
}

做了下簡(jiǎn)化處理纲酗,畢竟原文注釋太多太長(zhǎng)了衰腌,從官方給出的說明可以看到,一個(gè)是進(jìn)行請(qǐng)求數(shù)據(jù)處理的觅赊,一個(gè)是相應(yīng)數(shù)據(jù)處理的右蕊。ResponseWriter是個(gè)接口,只要實(shí)現(xiàn)了這個(gè)接口就可以吮螺,net/http/server.go 文件里面有對(duì)應(yīng)的實(shí)現(xiàn)方法饶囚,可以自己看下帕翻,而request是個(gè)結(jié)構(gòu)體變量。我們都知道變量需要初始化賦值才可用萝风,但從示例里面似乎沒看到在哪賦值嘀掸,直接HandleFunc 拿來就可以用了,那么賦值這一步操作在哪呢规惰,只有兩個(gè)方法睬塌,一個(gè)是調(diào)用,那么初始化這一步只可能在ListenAndServe方法里了卿拴,我們進(jìn)入ListenAndServe方法里面看看衫仑。

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

這里面沒有,只是初始化了服務(wù)器變量堕花,我們繼續(xù)進(jìn)入 server.ListenAndServe()

return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})

里面也沒有文狱,為了簡(jiǎn)化只貼出有用的代碼,我們繼續(xù)往下看缘挽。

for {
        rw, e := l.Accept()
        if e != nil {
            select {
            case <-srv.getDoneChan():
                return ErrServerClosed
            default:
            }
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)

這里面代碼就比較長(zhǎng)了瞄崇,我們只看有用的幾個(gè)
1、rw, e := l.Accept() //獲取一個(gè)連接請(qǐng)求壕曼,
2苏研、c := srv.newConn(rw) //連接初始化一下
3赚哗、 go c.serve(ctx) //啟動(dòng)goroutine 來處理請(qǐng)求
從上一篇net dial文件分析流部,我們知道獲取一個(gè)連接以后,就可以通過連接來讀取數(shù)據(jù)扰法,又因?yàn)檫@是http服務(wù)轧飞,為了處理多個(gè)連接衅鹿,所以go采用的是IO多路復(fù)用模式 ,用goroutine來處理請(qǐng)求过咬,我們繼續(xù)往下看大渤。

for {
        w, err := c.readRequest(ctx)
                ...
        ...
        serverHandler{c.server}.ServeHTTP(w, w.req)
                ...  
        w.cancelCtx()
        w.finishRequest()
        ...
    }

原方法太長(zhǎng)做了簡(jiǎn)化處理,具體代碼可自行查看掸绞,我們只看幾個(gè)主要的地方
1泵三、 w, err := c.readRequest(ctx)
2、 serverHandler{c.server}.ServeHTTP(w, w.req)
第一個(gè)方法就是初始化request變量的方法衔掸,在這個(gè)方法里初始化了request變量和response變量

func (c *conn) readRequest(ctx context.Context) (w *response, err error) 
req, err := readRequest(c.bufr, keepHostHeader)
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error)

這就是整個(gè)初始化request過程烫幕,通過這個(gè)方法,最終得到了Request變量敞映,里面包含http請(qǐng)求的參數(shù)纬霞,如head頭,url驱显,cookie等信息。傳遞到HandleFunc里面,就可以直接拿來使用了埃疫。
說到這里伏恐,還要再說個(gè)地方,我們知道調(diào)用HandleFunc 方法就可以把url請(qǐng)求綁定到處理方法上栓霜,那么綁定以后為什么訪問url的時(shí)候自動(dòng)就會(huì)調(diào)用綁定方法翠桦,就在于上面說的2:serverHandler{c.server}.ServeHTTP(w, w.req) 這里,

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

可以看到只要找到了url對(duì)應(yīng)的綁定方法胳蛮,就會(huì)調(diào)用销凑,從而就實(shí)現(xiàn)了整個(gè)http服務(wù)器流程。type Handler interface {
ServeHTTP(ResponseWriter, Request)
}
在handler接口只要實(shí)現(xiàn)了ServeHTTP就可以自定義屬于自己的http服務(wù)仅炊,我們熟知的Gin框架就是通過重寫ServeHTTP 實(shí)現(xiàn)自己封裝的
gin.Context結(jié)構(gòu)來傳遞信息斗幼。

總結(jié):go net/http包內(nèi)部還實(shí)現(xiàn)了很多處理,包括鎖抚垄,goroutine數(shù)量限制很多邏輯蜕窿,代碼結(jié)構(gòu)也封裝的很好,可以多看看學(xué)習(xí)學(xué)習(xí)呆馁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桐经,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浙滤,更是在濱河造成了極大的恐慌阴挣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纺腊,死亡現(xiàn)場(chǎng)離奇詭異畔咧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)摹菠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門盒卸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人次氨,你說我怎么就攤上這事蔽介。” “怎么了煮寡?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵虹蓄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我幸撕,道長(zhǎng)薇组,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任坐儿,我火速辦了婚禮律胀,結(jié)果婚禮上宋光,老公的妹妹穿的比我還像新娘。我一直安慰自己炭菌,他們只是感情好罪佳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著黑低,像睡著了一般赘艳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上克握,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天蕾管,我揣著相機(jī)與錄音,去河邊找鬼菩暗。 笑死掰曾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勋眯。 我是一名探鬼主播婴梧,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼客蹋!你這毒婦竟也來了塞蹭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤讶坯,失蹤者是張志新(化名)和其女友劉穎番电,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辆琅,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漱办,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婉烟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娩井。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖似袁,靈堂內(nèi)的尸體忽然破棺而出洞辣,到底是詐尸還是另有隱情,我是刑警寧澤昙衅,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布扬霜,位于F島的核電站,受9級(jí)特大地震影響而涉,放射性物質(zhì)發(fā)生泄漏著瓶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一啼县、第九天 我趴在偏房一處隱蔽的房頂上張望材原。 院中可真熱鬧沸久,春花似錦、人聲如沸华糖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽客叉。三九已至,卻和暖如春话告,著一層夾襖步出監(jiān)牢的瞬間兼搏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國打工沙郭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佛呻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓病线,卻偏偏與公主長(zhǎng)得像吓著,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子送挑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354