路由與http服務


本文主要講解go語言web編程中的路由與http服務基本原理。

首先,使用go語言啟動一個最簡單的http服務:

package main

import (
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", sayHello)
	log.Println("server running...")
	log.Fatal(http.ListenAndServe("localhost:4000", nil))
}

func sayHello(writer http.ResponseWriter, req *http.Request) {
	writer.Write([]byte("hello world!"))
}

編譯,運行,瀏覽器訪問 http://localhost:4000/ ,輸出 hello world! 。
總的來說,這段代碼只是做了兩件事情,第一,注冊路由,指定客戶端請求路徑對應的響應函數:
http.HandleFunc("/", sayHello)
第二,啟動http服務,監聽端口,接受並響應客戶端請求:
http.ListenAndServe("localhost:4000", nil)

 

先看第一件事情——注冊路由,指定請求路徑對應的響應函數。
首先看 http.HandleFunc() 函數源碼:

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

其中 DefaultServeMux 是go的默認路由器,所以注冊路由實際上是由路由器進行的,http.HandleFunc() 函數只是對它進行封裝,那么路由器的結構是怎么樣的呢?
源碼可見:

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry
	hosts bool
}
type muxEntry struct {
	h       Handler
	pattern string
}

其中 ServeMux 結構中的 map[string]muxEntry 就是用來保存請求路徑與響應函數之間的映射。從 muxEntry 結構定義可知,響應函數的類型為 Handler,而Handler實際上是一個接口類型,源碼如下:

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

所以,響應函數需要實現這個接口,才能進行路由注冊。
源碼中聲明了一個 HandlerFunc 類型,就實現了 Handler 接口:

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

所以,只要我們的響應函數滿足結構 func (http.ResponseWriter, *http.Request) ,即可進行路由注冊,注冊路由時,路由器會將其類型強制轉換為 HandlerFunc 。其中,http.ResponseWriter參數包含了響應頭、響應數據等響應相關信息,而http.Request參數則包含了請求頭、請求參數等請求相關信息。

 

再看第二件事情,啟動http服務,監聽端口,接受並響應客戶端請求。
首先看 http.ListenAndServe() 函數源碼:

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

其中 Server 即為http服務器類型,其結構如下(省略了部分字段):

type Server struct {
	Addr    string
	Handler Handler
......
}

其中 Addr 為服務器監聽的ip與端口字符串,Handler 為路由器,指定其為 nil 時,go會使用它的默認路由器 DefaultServeMux (調用 http.HandleFunc() 方法注冊路由時就是注冊到這個默認的路由器)。
服務器監聽端口,接受客戶端請求,並做出響應,這個過程可借助《go web編程》中的一張圖示來幫助理解:

圖中有兩個紅色矩形標記,第一個,說明針對客戶端的每一個請求,go都會使用一個Goroutine進行響應,保證每個請求都能獨立,相互不會阻塞,可以高效響應網絡事件;第二,最終調用默認路由器的 ServeHTTP(w ResponseWriter, r *Request) 方法進行路由,從請求路徑與響應函數的映射中找到對應的handler,最后調用handler的 ServeHTTP(w ResponseWriter, r *Request) 方法,從上面 HandlerFunc 類型的 ServeHTTP(w ResponseWriter, r *Request) 方法可知,其實最后調用的就是我們注冊路由時定義的響應函數本身。

使用go默認路由器的不足之處是,不滿足RESTful規則,而且對請求路徑的路由只支持絕對匹配,不支持正則匹配。如果想設計一些特殊、簡便的路由,需要設計一個自定義路由器,並讓go的http服務器使用這個自定義路由器。關於自定義路由器的設計,可以參考筆者另一篇博文:go web編程——自定義路由設計

 

 

借鑒:
《Go Web編程》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM