简单的http server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func myHandlerFunc(w http.ResponseWriter, r *http.Request){
fmt.Println("myHandlerFunc")
w.Write([]byte("From myHandlerFunc"))
}

type myHandler struct {

}

func (handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
fmt.Println("myHandler")
w.Write([]byte("From myHandler"))
}

func main(){
http.HandleFunc("/one",myHandlerFunc)
http.Handle("/two",&myHandler{})
http.ListenAndServe(":9090",nil)
}

注意到注册路由的两种方式,即http.HandleFunchttp.Handle

非成员方法会默认以DefaultServeMux为receiver,可见这两种方式最终都会调用DefaultServeMux.Handle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// server.go
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// 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)
}

ServeMux

ServeMux是HTTP request的multiplexer.所谓multiplexer,就是Server会处理对于多个不同url的请求,应该根据最长url匹配的原则把请求转发给指定的handler.DefaultServeMux是一个ServerMux的实例,在上例中调用全局的Handle HandleFunc等函数时,实际是在配置DefaultServeMux。监听时ListenAndServe的第二个参数留空,同样也是默认使用DefaultServeMux.

1
2
3
4
5
6
7
8
9
10
11
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
}

type muxEntry struct {
h Handler
pattern string
}

ServerMux中的mu字段维护了一个从pattern(路由表达式)到muxEntry的映射。muxEntry简单的把路由表达式和handler组合在一起。

Handler

Handler是一个接口,只有一个方法ServeHTTP().一个Handler(以及它的唯一方法)的职责是处理Request并构建Response.这也是golang的http服务暴露给开发者的入口,许多web框架自定义实现了Handler.

值得注意的是,ServeMux也实现了Handler接口,它将请求转发给最匹配的url所对应的handler。也就是说,我们可以使用ServeMux对象或者干脆使用DefaultServeMux来作为Handler.

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

小Tip

回到一开始ServeMuxHandleFunc成员函数:

1
2
3
4
5
6
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}

注意第5行的HandlerFunc(handler),它的作用是把一个具有func(Response Writer, *Request)签名的函数转变为实现了Handler接口的对象。

1
2
3
4
5
6
7
8
9
10
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}

通过一层wrapper优雅地实现了这一点,上述的HandlerFunc(handler)不是函数调用,而是一个类型转换。

回到DefaultServeMux.Handle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()

if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}

if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}

if pattern[0] != '/' {
mux.hosts = true
}
}

这个函数主要做了以下工作:

  1. 建立从url模式到对应handler的映射。
  2. 如果模式以'/'结尾,对应一个子树中所有的url,把它加入es列表中,按照最长有限匹配的原则从长到短排序。
  3. 不以'/'开头的模式表示只匹配某个主机名下的URL.

ListenAndServe

httpListenAndSere方法创建了一个Server对象并调用了其同名方法。ServerListenAndServe会初始化监听地址,调用Listen方法设置监听,把Listen返回的监听器对象传入Serve方法。Serve方法对每一个连接创建conn即连接对象,每个连接对象调用自己的serve方法,该方法中实际处理请求的语句是serverHandler{c.server}.ServeHTTP(w, w.req).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}

serverHandler开启了一系列调用流程,并最终调用了用户在ListenAndServe中指定的handler,如果为nil则会使用DefaultServeMux.

Best Practice

我们可以看出,为了把url模式绑定到指定的handler,我们有多种方式:

  1. 直接使用http.HandleFunc绑定,ListenAndServe时使用nil作为handler.这是使用了默认的DefaultServeMux.

  2. 使用HandlerFunc(注意之前提及的,这是一个强制类型转换的adaptor)把一个函数转化为Handler对象并用于ListenAndServe.

    1
    2
    3
    4
    5
    6
    func foo(w http.ResponseWriter, r *http.Request){
    //balabala
    }

    handler := http.HandlerFunc(foo)
    http.ListenAndServe(":9090",handler)
  3. 创建一个自定义的ServeMux,在里面注册handler.

    1
    2
    3
    4
    mux := http.NewServeMux()
    mux.HandleFunc("/one", HandlerOne)
    mux.HandleFunc("/two", HandlerTwo)
    http.ListenAndServe(":8001", mux)

建议使用第三种方式。

参考资料

  1. https://juejin.cn/post/6844903998869209095