前言
近期使用go開發(fā)api后臺服務(wù)宜鸯,對http模塊源碼有初步了解适滓,所以整理總結(jié)一下皂甘。
server端核心模型包括
- Server 類型結(jié)構(gòu)斧散,代表了一個(gè)指定端口下的服務(wù)供常。
同時(shí)也保存了tls,超時(shí)等配置鸡捐。最重要的是一個(gè)handler處理器栈暇。
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
TLSConfig *tls.Config
ReadTimeout time.Duration
//...
}
ListenAndServe方法開啟監(jiān)聽,等待連接箍镜。
server 監(jiān)聽客戶端請求源祈,啟動(dòng)goroutine處理。
func (srv *Server) Serve(l net.Listener) error {
//...
for {
rw, e := l.Accept()
// ...
c := srv.newConn(rw)
go c.serve(ctx)
}
}
- Handler 接口色迂,響應(yīng)一個(gè)http請求
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
- ServeMux : http請求多路復(fù)用器香缺,實(shí)現(xiàn)了 Handler接口,它的實(shí)現(xiàn)為: 轉(zhuǎn)發(fā)請求到匹配的handler處理歇僧。
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
- DefaultServeMux: 是ServeMux類型一個(gè)實(shí)力图张, 作為一個(gè)全局對象導(dǎo)出。當(dāng)新建的server對象诈悍,handler為空時(shí)祸轮,使用DefaultServeMux。
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
可以通過HandleFunc方法侥钳,向全局多路復(fù)用器中加入路由模式及其handler适袜。
處理請求時(shí)根據(jù)所有加入的路由模式中匹配。
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
注冊pattern, 如果以/結(jié)尾舷夺,會加入到排序的切片中苦酱,在map中匹配不到路由時(shí),使用它冕房,做最長匹配躏啰。
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
路由匹配規(guī)則趁矾,先在map中精確匹配耙册。
匹配失敗則 用到排序的slice 進(jìn)行最長匹配。
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
// Check for longest valid match. mux.es contains all patterns
// that end in / sorted from longest to shortest.
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
總結(jié):
- go的http server模塊同時(shí)也包含了處理socker 連接的代碼毫捣,所以不需要像java需要額外的應(yīng)用服務(wù)器详拙,例如tomcat。 go更易于部署蔓同,開發(fā)迭代饶辙。
我認(rèn)為一部分原因 是得益于go對并發(fā)的原生支持,go的并發(fā)模型斑粱。go開發(fā)時(shí)弃揽,可以使用goroute簡單 并且低代價(jià)的方式,即可獲得高并發(fā)的支持。
- go的http server模塊同時(shí)也包含了處理socker 連接的代碼毫捣,所以不需要像java需要額外的應(yīng)用服務(wù)器详拙,例如tomcat。 go更易于部署蔓同,開發(fā)迭代饶辙。
- go server中路由匹配 力度太大矿微,一個(gè)url匹配一個(gè)handler痕慢,不區(qū)分http方法,也無法在url中加入?yún)?shù)匹配涌矢。
所以 路由部分 還是建議使用開源模塊
https://github.com/gorilla/mux
https://github.com/julienschmidt/httprouter
mux源碼分析參考 : 這里
- go server中路由匹配 力度太大矿微,一個(gè)url匹配一個(gè)handler痕慢,不區(qū)分http方法,也無法在url中加入?yún)?shù)匹配涌矢。