golang中http server处理请求的过程梳理

之前说了几种golang中设置http server的方式,这里接着记录一下阅读源码时看到的一个server是如何处理用户请求的,还是从server的启动开始看起。

step 1:

初始化一个Server结构体实例后, 执行Server.ListenAndServer函数(其中主要调用net.Listen 和 Server.Serve函数)

step 2:

接着进入Server.Serve部分的代码,看看详细的处理过程,可以看到主要是在主goroutine中用for循环阻塞,不断通过Accept函数接收读取连接请求,然后调用Server.newConn()函数,创建一个连接实例c,然后每个连接实例启动一个新的goroutine去执行处理,从这里也能看出, 如果并发请求很高的时候,会创建出海量的goroutine来并发处理请求。

这一步,调用newConn函数,会创建一个conn结构体的示例客户端,其中的server成员变量是Server实例,即每个connection示例都保留了指向server的信息。

step 3:

接着我们进入每个connection处理的goroutine,看具体做了什么工作,这个函数有一百多行这里就不完整摘了,看看其主要的代码部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func (c *conn) serve(ctx context.Context) {
for{
...
w, err := c.readRequest(ctx)
...
req := w.req
...
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
}

}

从上面的代码片段可以看出, 先调用connection的readRequest函数构造一个response对象, 然后执行serverHandler{c.server}.ServeHTTP(w, w.req)进行实际的请求处理,后面我们来看看这个请求的过程。

step4:

先看看serverHandler的定义,可以看到其是一个结构体,有一个成员属性srv,结合step 3可以看出,serverHandler{c.server}.ServeHTTP(w, w.req)就是实例化了一个serverHandler结构体,然后执行其成员函数ServeHTTP,在ServeHTTP成员函数的定义中,可以看到,主要是调用了初始化server时的Handler成员属性,然后执行handler.ServeHTTP(rw, 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)
}

对于如下的例子,其实就是在调用aa.ServeHTTP(),因为此时的server.Handler就是aa这个结构体的实例。

1
2
3
4
5
6
7
8
9
type aa struct {
}

func (a aa) ServeHTTP(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("hhhhhh"))
}
func main() {
http.ListenAndServe(":8080", aa{})
}

对于使用serveMux的例子,这里的server.Handler就是如下serveMux的实例,这中方式下,用户自定义的处理函数都被类型转化为一个HandlerFunc类型,HandlerFunc同样实现了Handler接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// test3 我们可以增加一个ServeMux来做一个请求路由处理工作
func h(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("new pattern"))
}
func test3() {
mux := http.NewServeMux()

//通过HandleFunc函数去向mux中handler中注册处理函数
mux.HandleFunc("/test", func(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("test3333"))
})

// 直接调用Handle函数去注册处理函数,
// 这里的http.HandleFunc 是一个type类型,这里调用将h转换为一个Handler接口的实现
mux.Handle("/st", http.HandlerFunc(h))

// 这里会使用默认创建的server
http.ListenAndServe(":8082", mux)
}

serveMux结构体定义如下,从中可以看出它的ServeHTTP成员函数主要是根据URL pattern取出合适的Handler,这里的每个Handler都是一个HandlerFunc类型, 然后执行h.ServeHTTP(w, r)。

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
28
29
30
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
h Handler
pattern string
}

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


type HandlerFunc func(ResponseWriter, *Request)

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

golang中http server设置的几种方式记录:

.. 待续