Go自带一个HTTP包,可以满足Web开发中的一部分需求。但没有ORM、SESSION处理、缓存等。通过源码也发现该包的路由也很简单。但可以自定义路由。

问题:

  1. 如何根据请求路径找到对于的handler
  2. 路由如何处理
  3. 服务器从接收请求到处理请求的整个过程

一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"io"
"net/http"
"log"
)

func handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}

func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}

net/http分析源码,ListenAndServe创建一个Server实例,调用其ListenAndServe方法。

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

Handler是一个接口:

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

Server.ListenAndServe代码如下:

1
2
3
4
5
6
7
8
9
10
11
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

它监听TCP请求,使用Server.Serve接口处理TCP连接。tcpKeepAliveListener是一个结构体,用于保持TCP长连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
type tcpKeepAliveListener struct {
*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
tc, err := ln.AcceptTCP()
if err != nil {
return nil, err
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

Accept方法可以看到,长连接时间为三分钟。

再看看Server.Serve的实现

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
var tempDelay time.Duration // how long to sleep on accept failure

if err := srv.setupHTTP2_Serve(); err != nil {
return err
}

srv.trackListener(l, true)
defer srv.trackListener(l, false)

baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}

newConn代码如下,rwc是底层网络连接对象。

1
2
3
4
5
6
7
8
9
10
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}

忽略控制流程的细节代码,for循环部分在等待连接的到来,c := srv.newConn(rw)代码把TCP连接转化成用conn结构包装的对象。该结构代表服务器端的HTTP连接。因此,这里可以理解为把TCP连接包装成HTTP连接。该结构如下(列举部分元素):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type conn struct {
// server is the server on which the connection arrived.
// Immutable; never nil.
server *Server

// cancelCtx cancels the connection-level context.
cancelCtx context.CancelFunc

// rwc is the underlying network connection.
// This is never wrapped by other types and is the value given out
// to CloseNotifier callers. It is usually of type *net.TCPConn or
// *tls.Conn.
rwc net.Conn

// remoteAddr is rwc.RemoteAddr().String(). It is not populated synchronously
// inside the Listener's Accept goroutine, as some implementations block.
// It is populated immediately inside the (*conn).serve goroutine.
// This is the value of a Handler's (*Request).RemoteAddr.
remoteAddr string
}

conn.serve方法用于处理一个HTTP连接。这个方法有上百行代码,这里不全列出来。具体的方法签名如下:func (c *conn) serve(ctx context.Context)

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
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()

// ignore code here

for {
w, err := c.readRequest(ctx)

// Expect 100 Continue support
req := w.req
if req.expectsContinue() {
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
// Wrap the Body reader with one that replies on the connection
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
}
} else if req.Header.get("Expect") != "" {
w.sendExpectationFailed()
return
}

serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
}
}

for循环下c.readRequest获取具体的请求对象,然后根据HTTP语义处理。具体的路由和serverHandler结构有关。看看该结构的定义和其ServeHTTP代码:

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

默认使用DefaultServeMux处理路由,它是ServeMux结构的实例。

1
2
3
4
5
6
7
8
9
10
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
h Handler
pattern string
}

由此可见,Golang标准库net/http对路由的处理也很简单。

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

ServeMux中的ServeHTTP方法根据Handler方法找到处理该路由的函数,然后调用该HandlerServeHTTP,即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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}

return mux.handler(r.Host, r.URL.Path)
}

// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)

// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}

if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}

return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()

// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}

函数最终根据hostpath变量找到对应的路由。而match方法就简单粗暴直接使用map查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h
pattern = v.pattern
}
}
return
}

根据路由找到具体的处理函数后,调用ServeHTTP处理。

1
2
3
4
5
6
type HandlerFunc func(ResponseWriter, *Request)

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

而,注册路由的代码如下,本质上是把路径和对应的处理函数添加到map数据结构上。

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
31
32
33
34
35
36
37
38
39
40
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)
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

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

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
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)
}

完。