當前位置:
首頁 > 知識 > Go 語言 HTTP Server 源碼分析

Go 語言 HTTP Server 源碼分析

Go語言中HTTP Server:


HTTP server,顧名思義,支持http協議的伺服器,HTTP是一個簡單的請求-響應協議,通常運行在TCP之上。通過客戶端發送請求給伺服器得到對應的響應。

Go 語言 HTTP Server 源碼分析



HTTP服務簡單實現

package mainimport("fmt""net/http")//處理請求,返回結果funcHello(w http.ResponseWriter, r *http.Request){ fmt.Fprintln(w,"hello world")}funcmain(){//路由註冊http.HandleFunc("/",Hello)//服務監聽http.ListenAndServe(":8080",nil)}

Go 語言 HTTP Server 源碼分析



你以為這樣就結束了嗎,不才剛剛開始。


源碼分析


路由註冊


http中的HandleFunc方法,主要用來註冊路由


funcHandleFunc(pattern string, handlerfunc(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)}


DefaultServeMux是什麼?


DefaultServeMux是ServeMux的一個實例。

ServeMux又是什麼?


// DefaultServeMux is the default ServeMux used by Serve.varDefaultServeMux = &defaultServeMuxvardefaultServeMux ServeMuxtype ServeMuxstruct{ mu sync.RWMutex m map[string]muxEntry hostsbool}type muxEntrystruct{explicitboolh Handler patternstring}


很多地方都涉及到了Handler,那麼Handler是什麼?


type Handlerinterface{ ServeHTTP(ResponseWriter, *Request)}


此介面可以算是HTTP Server一個樞紐


func(mux *ServeMux)HandleFunc(pattern string, handlerfunc(ResponseWriter, *Request)) { mux.Handle(pattern,HandlerFunc(handler))}typeHandlerFuncfunc(ResponseWriter, *Request)func(f HandlerFunc)ServeHTTP(wResponseWriter, r *Request) { f(w, r)}


從代碼中可以看出HandlerFunc是一個函數類型,並實現了Handler介面。當通過調用HandleFunc(),把Hello強轉為HandlerFunc類型時,就意味著 Hello函數也實現ServeHTTP方法。


ServeMux的Handle方法:


func (mux *ServeMux) Handle(patternstring, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock()ifpattern ==""{ panic("http: invalid pattern "+ pattern) }ifhandler == nil { panic("http: nil handler") }ifmux.m[pattern].explicit{ panic("http: multiple registrations for "+ pattern) }ifmux.m == nil { mux.m = make(map[string]muxEntry) }//把handler和pattern模式綁定到//map[string]muxEntry的map上mux.m[pattern] = muxEntry{explicit:true, h: handler, pattern: pattern}ifpattern[] != / { mux.hosts =true}//這裡是綁定靜態目錄,不作為本片重點。n := len(pattern)ifn >&& pattern[n-1] == / && !mux.m[pattern[:n-1]].explicit{ path := patternifpattern[] != / { path = pattern[strings.Index(pattern,"/"):] } url := &url.URL mux.m[pattern[:n-1]] = muxEntry }}


上面的流程就完成了路由註冊。

服務監聽


typeServerstruct{AddrstringHandlerHandlerReadTimeouttime.DurationWriteTimeouttime.DurationTLSConfig*tls.ConfigMaxHeaderBytesintTLSNextProtomap[string]func(*Server, *tls.Conn, Handler)ConnStatefunc(net.Conn, ConnState)ErrorLog*log.LoggerdisableKeepAlives int32 nextProtoOnce sync.OncenextProtoErr error }funcListenAndServe(addr string, handler Handler)error { server := &Server{Addr: addr,Handler: handler}returnserver.ListenAndServe()}//初始化監聽地址Addr,同時調用Listen方法設置監聽。//最後將監聽的TCP對象傳入Serve方法:func(srv *Server)ListenAndServe() error { addr := srv.Addrifaddr ==""{ addr =":http"} ln, err := net.Listen("tcp", addr)iferr !=nil{returnerr }returnsrv.Serve(tcpKeepAliveListener) }


Serve(l net.Listener)為每個請求開啟goroutine的設計,保證了go的高並發。


func(srv *Server)Serve(l net.Listener) error {deferl.Close()iffn := testHookServerServe; fn !=nil{ fn(srv, l) }vartempDelay time.Duration// how long to sleep on accept failureiferr := srv.setupHTTP2_Serve(); err !=nil{returnerr } srv.trackListener(l,true)defersrv.trackListener(l,false) baseCtx := context.Background()// base is always background, per Issue 16220ctx := context.WithValue(baseCtx,ServerContextKey, srv) ctx = context.WithValue(ctx,LocalAddrContextKey, l.Addr())//開啟循環進行監聽for{//通過Listener的Accept方法用來獲取連接數據rw, e := l.Accept()ife !=nil{ select {casemax{ tempDelay =max} srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay)continue}returne } tempDelay =//通過獲得的連接數據,創建newConn連接對象c:= srv.newConn(rw)c.setState(c.rwc,StateNew)// before Serve can return//開啟goroutine發送連接請求goc.serve(ctx) }}


serve()為核心,讀取對應的連接數據進行分配


func(c*conn)serve(ctx context.Context) {c.remoteAddr =c.rwc.RemoteAddr().String()//連接關閉相關的處理deferfunc(){iferr := recover(); err !=nil&& err !=ErrAbortHandler{ const size =64


處理請求,返回結果


serverHandler 主要初始化路由多路復用器。如果server對象沒有指定Handler,則使用默認的DefaultServeMux作為路由多路復用器。並調用初始化Handler的ServeHTTP方法。


type serverHandlerstruct{ srv *Server}func(sh serverHandler)ServeHTTP(rwResponseWriter, req *Request) { handler := sh.srv.Handlerifhandler ==nil{ handler =DefaultServeMux}ifreq.RequestURI=="*"&& req.Method=="OPTIONS"{ handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}


這裡就是之前提到的匹配路由的具體代碼

func(mux *ServeMux)ServeHTTP(wResponseWriter, r *Request) {ifr.RequestURI=="*"{ifr.ProtoAtLeast(1,1) { w.Header().Set("Connection","close") } w.WriteHeader(StatusBadRequest)return}//匹配註冊到路由上的handler函數h,_:= mux.Handler(r)//調用handler函數的ServeHTTP方法//即Hello函數,然後把數據寫到http.ResponseWriter//對象中返回給客戶端。h.ServeHTTP(w, r)}func(mux *ServeMux)Handler(r *Request) (hHandler, pattern string) {ifr.Method!="CONNECT"{ifp := cleanPath(r.URL.Path); p != r.URL.Path{_, pattern = mux.handler(r.Host, p) url := *r.URLurl.Path= preturnRedirectHandler(url.String(),StatusMovedPermanently), pattern } }returnmux.handler(r.Host, r.URL.Path)}func(mux *ServeMux)handler(host, path string) (hHandler, pattern string) { mux.mu.RLock()defermux.mu.RUnlock()// Host-specific pattern takes precedence over generic onesifmux.hosts {//如 127.0.0.1/helloh, pattern = mux.match(host + path) }ifh ==nil{// 如 /helloh, pattern = mux.match(path) }ifh ==nil{ h, pattern =NotFoundHandler(),""}return}func(mux *ServeMux)match(path string) (hHandler, pattern string) {varn =fork, v := range mux.m {if!pathMatch(k, path) {continue}//通過迭代m尋找出註冊路由的patten模式//與實際url匹配的handler函數並返回。ifh ==nil|| len(k) > n { n = len(k) h = v.h pattern = v.pattern } }return}funcpathMatch(pattern, path string)bool {iflen(pattern) =={// should not happenreturnfalse} n := len(pattern)//如果註冊模式與請求uri一樣返回true,否則falseifpattern[n-1] != / {returnpattern == path }//靜態文件匹配returnlen(path) >= n && path[:n] == pattern}


將數據寫給客戶端


//主要代碼,通過層層封裝才走到這一步func(w checkConnErrorWriter)Write(p []byte) (n int, err error) { n, err = w.c.rwc.Write(p)iferr !=nil&& w.c.werr ==nil{ w.c.werr = err w.c.cancelCtx() }return}


serverHandler.ServeHTTP(w, w.req)當請求結束後,就開始執行連接斷開的相關邏輯。


總結


Go語言通過一個ServeMux實現了的路由多路復用器來管理路由。同時提供一個Handler介面提供ServeHTTP方法,實現handler介面的函數,可以處理實際request並返回response。


ServeMux和handler函數的連接橋樑就是Handler介面。ServeMux的ServeHTTP方法實現了尋找註冊路由的handler的函數,並調用該handler的ServeHTTP方法。


所以說Handler介面是一個重要樞紐。


簡單梳理下整個請求響應過程,如下圖

Go 語言 HTTP Server 源碼分析


最近熱文:


1、邀你參加11期 程序員專場相親活動


2、PHP高工/架構師,18-50K,深圳南山


3、JAVA/Web前端,深圳前海,15-30K


4、如何判斷是否到了該辭職的時候?


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序源 的精彩文章:

當寫爛代碼的人離職之後
推薦大家使用的CSS書寫規範、順序
堆排序以及最大優先隊列
Hibernate使用原生SQL適應複雜數據查詢
產品經理如何不被程序員嫌棄?

TAG:程序源 |

您可能感興趣

MFC TabSheet 源碼
PHP源碼分析之parse
Storm源碼分析之Trident源碼分析
JsBridge 源碼分析
APT組織Carbanak源碼泄露
JDK 源碼閱讀 Reference
LinkedList源碼分析
從JDK源碼看StringBuffer
Drill-on-YARN之源碼解析
PopupWindow源碼分析
Apache Storm流計算模型 及WordCount源碼實踐
JDK源碼閱讀:InterruptibleChannel 與可中斷 IO
Spark 源碼分析之ShuffleMapTask內存數據Spill和合併
React Native BackHandler exitApp 源碼分析
Thread源碼剖析
Android-IMX6Q源碼編譯
JDK 源碼閱讀 : DirectByteBuffer
HashMap源碼分析
《跟隨霄,LAMMPS源碼學習06》Atom:grow
小米MIX 3 Android P內核源碼公布