天天看點

Golang Http Server源碼閱讀前言幾個重要概念具體分析具體例子分析後記

這篇文章出現的理由是業務上需要建立一個Web Server。建立web是所有語言出現必須實作的功能之一了。在nginx+fastcgi+php廣為使用的今天,這裡我們不妨使用Go來進行web伺服器的搭建。

使用Go搭建Web伺服器的包有很多,大緻有下面幾種方法,直接使用net包,使用net.http包,使用第三方包(比如gorilla)。使用net包就需要從tcp層開始封裝,耗費人力物力極大,果斷舍棄。直接使用封裝好的net.http和第三方包才是上策。這裡我們就選擇了使用官方提供的net.http包來搭建web服務。另外附帶一句,gorilla的第三方包現在使用還是非常廣的,文檔也是比較全的,有興趣的同學可以考慮使用一下。

net.http包裡面有很多檔案,都是和http協定相關的,比如設定cookie,header等。其中最重要的一個檔案就是server.go了,這裡我們閱讀的就是這個檔案。

ResponseWriter: 生成Response的接口

Handler: 處理請求和生成傳回的接口

ServeMux: 路由,後面會說到ServeMux也是一種Handler

Conn : 網絡連接配接

(具體的說明直接以注釋形式放在代碼中)

1

2

3

<code>type Handler </code><code>interface</code> <code>{</code>

<code>    </code><code>ServeHTTP(ResponseWriter, *Request)  </code><code>// 具體的邏輯函數</code>

<code>}</code>

實作了handler接口的對象就意味着往server端添加了處理請求的邏輯。

下面是三個接口(ResponseWriter, Flusher, Hijacker):

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<code>// ResponseWriter的作用是被Handler調用來組裝傳回的Response的</code>

<code>type ResponseWriter </code><code>interface</code> <code>{</code>

<code>    </code><code>// 這個方法傳回Response傳回的Header供讀寫</code>

<code>    </code><code>Header() Header</code>

<code>    </code><code>// 這個方法寫Response的Body</code>

<code>    </code><code>Write([]</code><code>byte</code><code>) (</code><code>int</code><code>, error)</code>

<code>    </code> 

<code>    </code><code>// 這個方法根據HTTP State Code來寫Response的Header</code>

<code>    </code><code>WriteHeader(</code><code>int</code><code>)</code>

<code>// Flusher的作用是被Handler調用來将寫緩存中的資料推給用戶端</code>

<code>type Flusher </code><code>interface</code> <code>{</code>

<code>    </code><code>// 這個方法将寫緩存中資料推送給用戶端</code>

<code>    </code><code>Flush()</code>

<code>// Hijacker的作用是被Handler調用來關閉連接配接的</code>

<code>type Hijacker </code><code>interface</code> <code>{</code>

<code>    </code><code>// 這個方法讓調用者主動管理連接配接</code>

<code>    </code><code>Hijack() (net.Conn, *bufio.ReadWriter, error)</code>

<code> </code> 

實作這三個接口的結構是response(這個結構是http包私有的,在文檔中并沒有顯示,需要去看源碼)

<code>// response包含了所有server端的http傳回資訊</code>

<code>type response </code><code>struct</code> <code>{</code>

<code>    </code><code>conn          *conn         </code><code>// 儲存此次HTTP連接配接的資訊</code>

<code>    </code><code>req           *Request </code><code>// 對應請求資訊</code>

<code>    </code><code>chunking      </code><code>bool</code>     <code>// 是否使用chunk</code>

<code>    </code><code>wroteHeader   </code><code>bool</code>     <code>// header是否已經執行過寫操作</code>

<code>    </code><code>wroteContinue </code><code>bool</code>     <code>// 100 Continue response was written</code>

<code>    </code><code>header        Header   </code><code>// 傳回的http的Header</code>

<code>    </code><code>written       int64    </code><code>// Body的位元組數</code>

<code>    </code><code>contentLength int64    </code><code>// Content長度</code>

<code>    </code><code>status        </code><code>int</code>      <code>// HTTP狀态</code>

<code>    </code><code>needSniff     </code><code>bool</code>     <code>// 是否需要使用sniff。(當沒有設定Content-Type的時候,開啟sniff能根據HTTP body來确定Content-Type)</code>

<code>    </code><code>closeAfterReply </code><code>bool</code>     <code>//是否保持長連結。如果用戶端發送的請求中connection有keep-alive,這個字段就設定為false。</code>

<code>    </code><code>requestBodyLimitHit </code><code>bool</code> <code>//是否requestBody太大了(當requestBody太大的時候,response是會傳回411狀态的,并把連接配接關閉)</code>

在response中是可以看到

<code>func (w *response) Header() Header</code>

<code>func (w *response) WriteHeader(code </code><code>int</code><code>)</code>

<code>func (w *response) Write(data []</code><code>byte</code><code>) (n </code><code>int</code><code>, err error)</code>

<code>func (w *response) Flush()</code>

<code>func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error)</code>

這麼幾個方法。是以說response實作了ResponseWriter,Flusher,Hijacker這三個接口

handlerFunc是經常使用到的一個type

<code>// 這裡将HandlerFunc定義為一個函數類型,是以以後當調用a = HandlerFunc(f)之後, 調用a的ServeHttp實際上就是調用f的對應方法</code>

<code>type HandlerFunc func(ResponseWriter, *Request)</code>

<code>// ServeHTTP calls f(w, r).</code>

<code>func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {</code>

<code>    </code><code>f(w, r)</code>

這裡需要多回味一下了,這個HandlerFunc定義和ServeHTTP合起來是說明了什麼?說明HandlerFunc的所有執行個體是實作了ServeHttp方法的。另,實作了ServeHttp方法就是什麼?實作了接口Handler!

是以你以後會看到很多這樣的句子:

<code>func AdminHandler(w ResponseWriter, r *Request) {</code>

<code>    </code><code>...</code>

<code>handler := HandlerFunc(AdminHandler)</code>

<code>handler.ServeHttp(w,r)</code>

請不要訝異,你明明沒有寫ServeHttp,怎麼能調用呢? 實際上調用ServeHttp就是調用AdminHandler。

好吧,了解這個也花了我較長時間,附帶上一個play.google寫的一個小例子

<a href="http://play.golang.org/p/nSt_wcjc2u">http://play.golang.org/p/nSt_wcjc2u</a>

有興趣繼續研究的同學可以繼續試驗下去

如果你了解了HandlerFunc,你對下面兩個句子一定不會訝異了

<code>func NotFound(w ResponseWriter, r *Request) { Error(w, </code><code>"404 page not found"</code><code>, StatusNotFound) }</code>

<code>func NotFoundHandler() Handler { </code><code>return</code> <code>HandlerFunc(NotFound) }</code>

下面接着看Server.go

它就是http包中的路由規則器。你可以在ServerMux中注冊你的路由規則,當有請求到來的時候,根據這些路由規則來判斷将請求分發到哪個處理器(Handler)。

它的結構如下:

<code>type ServeMux </code><code>struct</code> <code>{</code>

<code>    </code><code>mu sync.RWMutex   </code><code>//鎖,由于請求設計到并發處理,是以這裡需要一個鎖機制</code>

<code>    </code><code>m  map[</code><code>string</code><code>]muxEntry  </code><code>// 路由規則,一個string對應一個mux實體,這裡的string就是我注冊的路由表達式</code>

下面看一下muxEntry

<code>type muxEntry </code><code>struct</code> <code>{</code>

<code>    </code><code>explicit</code> <code>bool</code>   <code>// 是否精确比對</code>

<code>    </code><code>h        Handler </code><code>// 這個路由表達式對應哪個handler</code>

看到這兩個結構就應該對請求是如何路由的有思路了:

ServeMux定義的方法有:

<code>func (mux *ServeMux) match(path </code><code>string</code><code>) Handler   </code><code>//根據path擷取Handler</code>

<code>func (mux *ServeMux) handler(r *Request) Handler  </code><code>//根據Request擷取Handler,内部實作調用match</code>

<code>func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) </code><code>//!!這個說明,ServeHttp也實作了Handler接口,它實際上也是一個Handler!内部實作調用handler</code>

<code>func (mux *ServeMux) Handle(pattern </code><code>string</code><code>, handler Handler) </code><code>//注冊handler方法</code>

<code>func (mux *ServeMux) HandleFunc(pattern </code><code>string</code><code>, handler func(ResponseWriter, *Request))  </code><code>//注冊handler方法(直接使用func注冊)</code>

在godoc文檔中經常見到的DefaultServeMux是http預設使用的ServeMux

var DefaultServeMux = NewServeMux()

如果我們沒有自定義ServeMux,系統預設使用這個ServeMux。

換句話說,http包外層(非ServeMux)中提供的幾個方法:

<code>func Handle(pattern </code><code>string</code><code>, handler Handler) { DefaultServeMux.Handle(pattern, handler) }</code>

<code>func HandleFunc(pattern </code><code>string</code><code>, handler func(ResponseWriter, *Request)) {</code>

<code>    </code><code>DefaultServeMux.HandleFunc(pattern, handler)</code>

實際上就是調用ServeMux結構内部對應的方法。

下面還剩下一個Server結構

<code>type Server </code><code>struct</code> <code>{</code>

<code>    </code><code>Addr           </code><code>string</code>        <code>// 監聽的位址和端口</code>

<code>    </code><code>Handler        Handler       </code><code>// 所有請求需要調用的Handler(實際上這裡說是ServeMux更确切)如果為空則設定為DefaultServeMux</code>

<code>    </code><code>ReadTimeout    time.Duration </code><code>// 讀的最大Timeout時間</code>

<code>    </code><code>WriteTimeout   time.Duration </code><code>// 寫的最大Timeout時間</code>

<code>    </code><code>MaxHeaderBytes </code><code>int</code>           <code>// 請求頭的最大長度</code>

<code>    </code><code>TLSConfig      *tls.Config   </code><code>// 配置TLS</code>

Server提供的方法有:

<code>func (srv *Server) Serve(l net.Listener) error   </code><code>//對某個端口進行監聽,裡面就是調用for進行accept的處理了</code>

<code>func (srv *Server) ListenAndServe() error  </code><code>//開啟http server服務,内部調用Serve</code>

<code>func (srv *Server) ListenAndServeTLS(certFile, keyFile </code><code>string</code><code>) error </code><code>//開啟https server服務,内部調用Serve</code>

當然Http包也直接提供了方法供外部使用,實際上内部就是執行個體化一個Server,然後調用ListenAndServe方法

<code>func ListenAndServe(addr </code><code>string</code><code>, handler Handler) error   </code><code>//開啟Http服務</code>

<code>func ListenAndServeTLS(addr </code><code>string</code><code>, certFile </code><code>string</code><code>, keyFile </code><code>string</code><code>, handler Handler) error </code><code>//開啟HTTPs服務</code>

<code>func HelloServer(w http.ResponseWriter, req *http.Request) {</code>

<code>    </code><code>io.WriteString(w, </code><code>"hello, world!\n"</code><code>)</code>

<code>func main() {</code>

<code>    </code><code>http.HandleFunc(</code><code>"/hello"</code><code>, HelloServer)</code>

<code>    </code><code>err := http.ListenAndServe(</code><code>":12345"</code><code>, nil)</code>

<code>    </code><code>if</code> <code>err != nil {</code>

<code>        </code><code>log.Fatal(</code><code>"ListenAndServe: "</code><code>, err)</code>

<code>    </code><code>}</code>

按順序做了幾件事:

1 調用了DefaultServerMux的HandleFunc

2 調用了DefaultServerMux的Handle

3 往DefaultServeMux的map[string]muxEntry中增加對應的handler和路由規則

按順序做了幾件事情:

1 執行個體化Server

2 調用Server的ListenAndServe()

3 調用net.Listen("tcp", addr)監聽端口

4 啟動一個for循環,在循環體中Accept請求

5 對每個請求執行個體化一個Conn,并且開啟一個goroutine為這個請求進行服務go c.serve()

6 讀取每個請求的内容w, err := c.readRequest()

7 判斷header是否為空,如果沒有設定handler(這個例子就沒有設定handler),handler就設定為DefaultServeMux

8 調用handler的ServeHttp

9 在這個例子中,下面就進入到DefaultServerMux.ServeHttp

10 根據request選擇handler,并且進入到這個handler的ServeHTTP

       mux.handler(r).ServeHTTP(w, r)

11 選擇handler:

    A 判斷是否有路由能滿足這個request(循環周遊ServerMux的muxEntry)

    B 如果有路由滿足,調用這個路由handler的ServeHttp

    C 如果沒有路由滿足,調用NotFoundHandler的ServeHttp

對于net.http包中server的了解是非常重要的。理清serverMux, responseWriter, Handler, HandlerFunc等常用結構和函數是使用go web的重要一步。個人感覺由于go中文檔較少,像這樣有點複雜的包,看godoc的效果就遠不如直接看代碼來的快和清晰了。實際上在了解了http包後,才會對godoc中出現的句子有所了解。後續還會寫一些文章關于使用net.http建構web server的。請期待之。

本文轉自軒脈刃部落格園部落格,原文連結:http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html,如需轉載請自行聯系原作者