如果要更深入的理解golanghttp处理,那么net/http包的源码就得研读一番。

我们使用原生的golang来处理http处理,一般我们的代码是这样写的。

 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
package main

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

func main() {
	// set route and handler  设置路由及处理请求
	http.HandleFunc("/hello", helloHandle)
	http.HandleFunc("/test", testHandle)
	http.HandleFunc("/", indexHandle)
	fmt.Println("Listening at port 8080...")
	// listen at port 8080  监听8080端口
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func indexHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "index")
}

func helloHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "hello world")
}

func testHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "test...")
}

我们可以很容易的启动一个http服务器,并处理相关的http请求。在上面的代码中,我们使用到了http包中的两个方法,一个是HandleFuc,一个是ListenAndServe

可以从net/http包下的server.go中找到对应的代码:

1
2
3
4
5
6
// 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)
}

看到上面一段代码,我们发现HandleFunc实际上是将所有权交给serveMuxserveMux又是什么?简单点说,serveMux是一个多路复用器,其作用就是将 请求的路由与handler绑定。

1
2
3
4
5
6
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
}

从结构上看,serveMux将路由以map的方法存储至m中,mu为了防止并发加的锁,当我们的请求的uri到达后,如果没有自定义mux,那将会走defaultMux进行路由匹配, mux会调用其match方法,找到对应的muxEntry,然后处理对应的请求。

通过上面的分析,我们知道HandleFunc只是用于将路由绑定至mux中,而mux如果我们不自己定义,就是走的defaultServeMux。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

ListenAndServe 是将http服务启动,监听相应的端口,该方法将初始化Server结构体。

  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
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
type Server struct {
    // Addr optionally specifies the TCP address for the server to listen on,
    // in the form "host:port". If empty, ":http" (port 80) is used.
    // The service names are defined in RFC 6335 and assigned by IANA.
    // See net.Dial for details of the address format.
    Addr string
    
    Handler Handler // handler to invoke, http.DefaultServeMux if nil
    
    // TLSConfig optionally provides a TLS configuration for use
    // by ServeTLS and ListenAndServeTLS. Note that this value is
    // cloned by ServeTLS and ListenAndServeTLS, so it's not
    // possible to modify the configuration with methods like
    // tls.Config.SetSessionTicketKeys. To use
    // SetSessionTicketKeys, use Server.Serve with a TLS Listener
    // instead.
    TLSConfig *tls.Config
    
    // ReadTimeout is the maximum duration for reading the entire
    // request, including the body. A zero or negative value means
    // there will be no timeout.
    //
    // Because ReadTimeout does not let Handlers make per-request
    // decisions on each request body's acceptable deadline or
    // upload rate, most users will prefer to use
    // ReadHeaderTimeout. It is valid to use them both.
    ReadTimeout time.Duration
    
    // ReadHeaderTimeout is the amount of time allowed to read
    // request headers. The connection's read deadline is reset
    // after reading the headers and the Handler can decide what
    // is considered too slow for the body. If ReadHeaderTimeout
    // is zero, the value of ReadTimeout is used. If both are
    // zero, there is no timeout.
    ReadHeaderTimeout time.Duration
    
    // WriteTimeout is the maximum duration before timing out
    // writes of the response. It is reset whenever a new
    // request's header is read. Like ReadTimeout, it does not
    // let Handlers make decisions on a per-request basis.
    // A zero or negative value means there will be no timeout.
    WriteTimeout time.Duration
    
    // IdleTimeout is the maximum amount of time to wait for the
    // next request when keep-alives are enabled. If IdleTimeout
    // is zero, the value of ReadTimeout is used. If both are
    // zero, there is no timeout.
    IdleTimeout time.Duration
    
    // MaxHeaderBytes controls the maximum number of bytes the
    // server will read parsing the request header's keys and
    // values, including the request line. It does not limit the
    // size of the request body.
    // If zero, DefaultMaxHeaderBytes is used.
    MaxHeaderBytes int
    
    // TLSNextProto optionally specifies a function to take over
    // ownership of the provided TLS connection when an ALPN
    // protocol upgrade has occurred. The map key is the protocol
    // name negotiated. The Handler argument should be used to
    // handle HTTP requests and will initialize the Request's TLS
    // and RemoteAddr if not already set. The connection is
    // automatically closed when the function returns.
    // If TLSNextProto is not nil, HTTP/2 support is not enabled
    // automatically.
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    
    // ConnState specifies an optional callback function that is
    // called when a client connection changes state. See the
    // ConnState type and associated constants for details.
    ConnState func(net.Conn, ConnState)
    
    // ErrorLog specifies an optional logger for errors accepting
    // connections, unexpected behavior from handlers, and
    // underlying FileSystem errors.
    // If nil, logging is done via the log package's standard logger.
    ErrorLog *log.Logger
    
    // BaseContext optionally specifies a function that returns
    // the base context for incoming requests on this server.
    // The provided Listener is the specific Listener that's
    // about to start accepting requests.
    // If BaseContext is nil, the default is context.Background().
    // If non-nil, it must return a non-nil context.
    BaseContext func(net.Listener) context.Context
    
    // ConnContext optionally specifies a function that modifies
    // the context used for a new connection c. The provided ctx
    // is derived from the base context and has a ServerContextKey
    // value.
    ConnContext func(ctx context.Context, c net.Conn) context.Context
    
    inShutdown atomicBool // true when server is in shutdown
    
    disableKeepAlives int32     // accessed atomically.
    nextProtoOnce     sync.Once // guards setupHTTP2_* init
    nextProtoErr      error     // result of http2.ConfigureServer if used
    
    mu         sync.Mutex
    listeners  map[*net.Listener]struct{}
    activeConn map[*conn]struct{}
    doneChan   chan struct{}
    onShutdown []func()
}

从server的结构中可以发现包含handler类型,从server的handler的注释中可以看出,handler就是用于多路复用路由处理的,Handler是一个接口类型,只要 实现了Handler接口的方法,都可以当作server的handler,从这里可以发现,我们如果不想用defaultServeMux来做多路复用处理器,我们自定义的一个CustomerHandler实现 ServeHTTP(ResponseWriter, *Request)方法即可。 Handler接口定义如下:

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

httpServer的接收到request处理的过程是和复杂的,目前也不太想细细研究这部分的内容,知道经过一系列的处理,最终将处理的能力交给实现了ServeHTTP的handler即可。