Go的http包詳解

前面文章我們已經(jīng)實現(xiàn)了一個簡單的Web服務(wù)⊙笄龋現(xiàn)在我們詳細解剖http包边苹,分析內(nèi)部實現(xiàn)的細節(jié)个束。

1.1 http包中重要的類型和接口:

  • server:HTTP服務(wù)器茬底,定義監(jiān)聽的地址、端口,處理器等信息。
  • conn:用戶每次請求的鏈接谨胞。
  • response:返回給用戶的信息胯努。
  • request:用戶請求的信息逢防。
  • Handler: 處理器灰署,用戶請求到來時溉箕,服務(wù)器的處理邏輯,它是一個包含ServeHTTP方法的接口悦昵。

1.2 http包的運行流程:

image.png

2.1 http包如何實現(xiàn)高并發(fā)

http包中肴茄,server每接收一個用戶請求,都會生成一個conn鏈接但指,并生成一個goroutines來處理對應(yīng)的conn寡痰。所以每個請求都是獨立的,相互不阻塞的棋凳。

c := srv.newConn(rw)
c.setState(c.rwc, StateNew) 
go c.serve(ctx)

2.2 處理器(Handler)和多路復(fù)用器(ServeMux)

前一小節(jié)我們使用ListenAndServe啟動服務(wù)拦坠,第二個參數(shù)是一個處理器。但是我們傳入的是一個nil剩岳,http是如何工作的呢贞滨?

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

所以當我們的處理器為空時,http包會默認幫我們生成一個DefaultServeMux處理器拍棕。
不是說第二個參數(shù)是處理器嗎疲迂,但是DefaultServeMux是一個ServeMux結(jié)構(gòu)的實例, 是一個多路復(fù)用器。這是怎么回事莫湘?
其實處理器是一個擁有ServeHTTP方法的接口,只要實現(xiàn)了這個方法郑气,它就是一個處理器幅垮。所以DefaultServeMux不僅是一個多路復(fù)用器,而且還是一個處理器尾组。只是這個處理器比較特殊忙芒,它只是根據(jù)請求的URL將請求分配到對應(yīng)的處理器示弓。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

我們可以自定義自己的處理器

package main

import (
    "fmt"
    "net/http"
)

// 定一個自定義的Handler的結(jié)構(gòu)
type MyHanlder struct{}

// 為MyHanlder結(jié)構(gòu)實現(xiàn)Hanlder接口的ServeHTTP的方法,此時MyHandler將是一個處理器(Handler)
func (h MyHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This my handler")
}

func main() {
    // 實例化MyHandler
    hanlder := MyHanlder{}

    //啟動服務(wù)器并監(jiān)聽8000/tcp端口
    err := http.ListenAndServe(":8000", hanlder)
    if err != nil {
        fmt.Println(err)
    }
}

2.3 ServeMux和DefaultServeMux

前面我們說DefaultServeMux是一個ServeMux的實例呵萨,它不僅是一個多路復(fù)用器奏属,而且是一個處理器。多路復(fù)用器具有路由功能潮峦,可以根據(jù)請求的URL將請求傳遞給對應(yīng)的處理器處理囱皿。看下http包中默認的多路復(fù)用器是怎么實現(xiàn)的:

type ServeMux struct {
    mu    sync.RWMutex  //鎖忱嘹,應(yīng)為請求是并發(fā)處理的嘱腥,所以這里需要鎖機制
    m     map[string]muxEntry //路由規(guī)則的map,一個string對應(yīng)一個muxEntry
    es    []muxEntry 
    hosts bool       
}

type muxEntry struct {
    h       Handler    //string對應(yīng)的處理器
    pattern string     //匹配的字符串
}

默認的多路復(fù)用器是根據(jù)請求的URL與存儲的map做匹配拘悦,匹配到了就返回對應(yīng)的handler齿兔,然后調(diào)用該handler的ServeHTTP方法來處理請求。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

    if r.Method == "CONNECT" {
        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
        }

        return mux.handler(r.Host, r.URL.Path)
    }

    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)

    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    }

    if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        url := *r.URL
        url.Path = path
        return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    }

    return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

我們也可以根據(jù)接口定義實現(xiàn)自己簡單的路由器

package main

import (
    "fmt"
    "net/http"
)

// 自定義一個多路復(fù)用器的結(jié)構(gòu)
type MyMux struct{}

// 實現(xiàn)ServeHTTP方法
func (m MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 判斷URL并轉(zhuǎn)到對應(yīng)的handler處理
    if r.URL.Path == "/index" {
        IndexHandler(w, r)
        return
    }

    http.NotFound(w, r)
    return
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This is index page")
}

func main() {
    // 實例化一個自定義的路由器
    mux := MyMux{}
    err := http.ListenAndServe(":8000", mux)
    if err != nil {
        fmt.Println(err)
    }
}

代碼傳送門

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末础米,一起剝皮案震驚了整個濱河市分苇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屁桑,老刑警劉巖医寿,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掏颊,居然都是意外死亡糟红,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門乌叶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盆偿,“玉大人,你說我怎么就攤上這事准浴∈屡ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵乐横,是天一觀的道長求橄。 經(jīng)常有香客問我,道長葡公,這世上最難降的妖魔是什么罐农? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮催什,結(jié)果婚禮上涵亏,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好气筋,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布拆内。 她就那樣靜靜地躺著,像睡著了一般宠默。 火紅的嫁衣襯著肌膚如雪麸恍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天搀矫,我揣著相機與錄音抹沪,去河邊找鬼。 笑死艾君,一個胖子當著我的面吹牛采够,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冰垄,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蹬癌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虹茶?” 一聲冷哼從身側(cè)響起逝薪,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝴罪,沒想到半個月后董济,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡要门,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年虏肾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欢搜。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡封豪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炒瘟,到底是詐尸還是另有隱情吹埠,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布疮装,位于F島的核電站缘琅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏廓推。R本人自食惡果不足惜刷袍,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望樊展。 院中可真熱鬧呻纹,春花似錦鸽心、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藤肢。三九已至太闺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘁圈,已是汗流浹背省骂。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留最住,地道東北人钞澳。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像涨缚,于是被迫代替她去往敵國和親轧粟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容