Golang net/http原始碼筆記/路由運作原理
This is a walkthrough about the HTTP multiplexer(router) in the net/http package. Let me know if I need to write this in English.
熟悉Golang的朋友都知道,一個簡單的webserver的寫法大概如下
簡單易懂,只要符合http.ResponseWriter, *http.Request的function都可以被當作routing handler,但這篇主要不是介紹他的用法,而是介紹它的原始碼,它底層是如何運作,開始之前先說這片是講net/http包,而不是其他功能較完備的第三方router,如gorilla/mux和httprouter等等
以下正文&一大堆Source Code:
這是在呼叫ListenAndServe後所發生的事情,基本上該注意的地方只有Handler(定義參考下面)那邊,一般來說第二個參數給nil,並使用內建的Router就可,而在這邊若有實現自己的Router,則會在這邊創建為server的handler,之後server會呼叫自己的ListenAndServe,基本上就是一些tcp的連線,如呼叫net.Tcp(“tcp”, addr),並開一個for loop去每一個請求都另起一個goroutine並呼叫
來處理每個連線。
題外話,很有名的fasthttp有實作自己的workerpool,類似線程池的概念,因此據說比net/http快很多,這邊我並沒有看過原始碼也沒有實際玩過,而且這篇的重點並不是在tcp建立連線等等,在此不多做討論,以router相關原始碼為主。
在每個context.serve底下,處理完io和底層的一些tcp後,便呼叫以下的函數
這邊handler就是剛剛ListenAndServe第二個參數所傳進去的值,若沒有傳入則用預設值,而預設值其實就是底下的ServeMux。muxEntry則是一個map用來儲存([路徑]routing func)。最後Handler 是任意一個實現ServeHttp方法的interface。
而在剛剛server.ServeHttp的最後一行,可以看到以下mux.ServeHttp被呼叫
這個方法沒什麼特別的,基本上只是看看Request URI是否合法,之後便呼叫mux的Handler函數以取得routing function和URL pattern,
而函數中間也只是一些URL的解析,如:取得port和host、以及把www.xyz.com/tree轉至www.xyz.com/tree/等等。之後在最後一行呼叫mux.handler方法
這裡會再呼叫match方法,其實就是進muxEntry的map裡面找看看當前的path有沒有符合的routing function,若沒有完全一致的則找最長符合字串,如/category/apple/會比/category/還要好,有興趣的可以自己參考原始碼中的mux.match函數。
到這邊你可能會問,如果不寫自己的mux(router),那開頭的http.HandleFunc是怎麼把routing function給寫進去的呢?
答案就在以下這裡
這邊呼叫http.HandleFunc會把你寫的routing func直接傳給預設的mux
而你所寫的routing function等於HandleFunc這個type,然後藉由剛剛mux.ServeHTTP函數的最後一行h.ServeHTTP呼叫這個函數,最後在這個函數裡呼叫你所提供的routing function!
總結,這整個路由的過程大概如下
- 首先用http.HandleFunc註冊routing func
- 先後呼叫了DefaultServeMux的HandleFunc和Handle,並往handler 也就是map[string]muxEntry中註冊對應的路由規則
- 創建server並呼叫ListenAndServe
- 進入serveHandler.ServeHTTP以後若有自己實現的mux則使用自定的mux,沒有則使用預設的DefaultServeMux
- 呼叫mux.ServeHTTP
- 呼叫mux.Handler解析URL相關資訊,如host等
- 呼叫mux.handler去muxEntry中比對符合的路徑並回傳相應的handler
- 呼叫handler.ServeHTTP,要注意的是這邊它已經幫你轉換型別至HandlerFunc,並實現ServeHTTP,因此由這個函數呼叫你所寫好的routing function
大概是這樣,剛寫golang不到一個月,若有哪邊不懂或是有誤再請各位告知,感謝~