標準庫net/http包使用及工作原理

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

標準庫net/http包使用及工作原理

一. 1個 HTTP Server的基本構成

一個Web應用從 瀏覽器向伺服器發送請求(Request)開始,到伺服器根據請求做出相應響應(Response)結束,整個流程中伺服器的控制核心無疑是:伺服器從HTTP Request中提取請求路徑(URL)並找到對應的處理常式(Handler)處理請求,最後返回結果。

我們知道HTTP協議是基於TCP協議的,我們當然可以自己通過標準庫net中提供TCP API開始寫一個HTTP Server,但你不得不自己額外做的事有:

  1. 實現TCP Server監聽,為每一新來的TCP link建立一個goroutine, 並在goroutine與用戶端互動(不用擔心單機C10K問題,因為goroutine是使用者態線程,很輕量級,可以很隨意就建立成千上萬個)。
  2. 在每個goroutine中將TCP link中的請求資料按HTTP協議格式解析出來(可以將資料解析出成Request對象,以後的訪問提供方便),並根據其URL找到相應的處理常式Handler, 因此你還需要提前建立好 URL:Handler映射表
  3. 當處理常式處理結束後,你還需要將處理結果資料按HTTP協議格式返回給用戶端。代碼大致如下:
//建立 URL:Handler映射表//注意:由於table會在不同goroutine中使用,因此真正環境中需要鎖保護var table = map[string]Handler {    "/": rootHandler,    "/login": loginHandler,    ... } //Server監聽ln, _ := net.Listen("tcp", ":8080")for {    conn, _ := ln.Accept()    go handleConn(conn)}//請求處理func handleConn(conn net.Conn) {    //1. 將請求成封裝Request對象    //2. 從table中尋找相應處理常式    //3. 將處理結果封裝HTTP格式資料返回給用戶端}

然而這些需求在net/http包中都滿足了,為什麼不直接用?(Don't Repeat Yourself)
net/http包中幾個重要的類型:
http.ServeMux: 建立URL:Handler映射表
http.Server: 運行HTTP Server
http.Request: 封裝用戶端HTTP請求資料
http.ResponseWriter: 用來構造伺服器端HTTP響應資料
http.Handler: URL處理常式必須實現的介面

二. 使用net/http搭建一個最簡單的HTTP Server

//step1. 建立 URL:Handler映射表mux := http.NewServeMux()mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hello, world")})//step2. 建立並運行HTTP serverserver := http.Server{Addr: ":8080", Handler: mux}log.Fatal(server.ListenAndServe())

然後我們開啟瀏覽器在地址欄輸入:http://localhost:8080
伺服器將返回:Hello, world
是不是so easy!!!

三. 第二部分代碼內部工作原理

第二部分代碼和第一部分工作原理基本一致。首先看看step1中的 mux(http.ServeMux)的定義:

type ServeMux struct {mu    sync.RWMutex        //保護mm     map[string]muxEntry //URL:Handler映射表hosts bool}type muxEntry struct {explicit boolh        Handlerpattern  string}

很顯然當我們調用mux.HandleFunc(...)的時候就是添加URL:Handler索引值對到mux的map中。那麼問題來了,func(http.ResponseWriter,*http.Request)是個函數類型,而http.Handler是一個介面類型,二者是如何轉換的?不妨看看 mux.HandleFunc:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {mux.Handle(pattern, HandlerFunc(handler))  //又調用了mux.Handle}

而 mux.Handle 就比較簡單了,就是將 func(http.ResponseWriter,*http.Request)轉換為 http.Handler 然後放入mux的map中。

HandlerFunc(注意不要與 HandleFunc 函數混淆了)是個什麼鬼?又有什麼用?

type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {    f(w, r)}

其實就是一個實現了 http.Handler 介面的類型,該類型底層基礎類型就是 func(ResponseWriter, *Request),我們知道在go語言中除了 指標與介面 其他基礎類型也是可以定義方法的,標準庫定義這個一個類型,為的就是將 普通func(ResponseWriter, *Request) 適配到 http.Handler介面. 所以 HandlerFunc 這樣的類又稱為適配器類,這一做法很有用,一定要掌握住該技巧。

好了,現在看看step2的 http.Server,將 監聽地址 和 step1中的 http.ServeMux 對象傳遞給了它,然後調用server.ListenAndServe()開始監聽, 處理流程大致如下:

  1. server監聽到有新連結進來,建立一個goroutine來處理新連結
  2. 在goroutine中,將請求和響應分別封裝為 http.Request和http.ResponseWriter對象。然後用這兩個對象作為函數參數調用 server.Handler.serveHTTP(...), 而server.Handler 即為我們傳入的 http.ServeMux 對象,而http.ServeMux對象的serveHTTP方法,我們都沒有碰過,裡面到底做了什嗎?
  3. http.ServeMux對象的serveHTTP方法做的事,其實就是根據 http.Request對象中的URL 在自己的map中尋找對應的Handler(這個又是我們在step1中添加的),然後執行。

繞了一大圈,簡單來說就是 每當有新請求進來,server都會為我們建立一個goroutine,並在其中根據請求URL調用 我們在建立server之前添加的 URL:Handler映射表(通過server中的http.Handler欄位混入)中的相應URL的Handler.

問題:為什麼不在server中放置一個 URL:Handler 映射表?
這樣上面步驟2中就不用先繞到 server.Handler.serveHTTP(...)中,才能尋找映射表了。這樣的話http.ServeMux 對象也不需要了。 這樣做從程式邏輯上講沒有問題,但將http.Server的邏輯弄得更複雜了,通過一個 http.Handler中介層URL路由功能從http.Server 解耦出來,雖然理解起來有點繞,但各自的職責將更加清楚(http.Server就是只管HTTP Server中通用功能的部分,商務邏輯的不同處理都通過 http.ServeMux 來構建)

四. 更簡單的代碼寫法

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hello, world")})log.Fatal(http.ListenAndServe(":8080", nil))

為什麼連 http.ServeMuxhttp.Server都沒有用到?其實肯定是需要的,只是被封裝了起來。大家都說golang很容易上手,這其實得益於 實現golang的團隊 強大的封裝抽象能力,將複雜留給了自己,簡單易用性給了開發人員。

其實這種寫法是使用了 net/http包中定義的一個 全域http.ServeMux物件變數 DefaultServeMux, 然後在 http.ListenAndServe(":8080", nil) 函數中新構建了一個 http.Server對象,然後讓server進入監聽。比較巧妙地是 函數http.ListenAndServe的第二個參數,如果是nil,該server才會使用DefaultServeMux,否則使用新傳入的http.ServeMux對象。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.