net/http
包详解在上一篇文章中我们已经使用 net/http
(以下简称 http
) 创建了一个 Web 服务,并从源码层面分析了整个请求流转的过程,其中有两个比较核心的组件或者功能,一个是连接 Conn,另外一个是 ServeMux。
在 http
包中使用 goroutine
来处理连接的读写,这样既可以使每个请求保持独立,请求相互之间不会影响,不会阻塞,可以高效的响应网络请求,有可以实现高并发和高性能。Go 在等待客户端请求的代码如下:
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
go c.serve(connCtx)
会为每一个请求创建一个 Conn,这个 Conn 中保存了该次请求的信息,然后在通过 serve 函数传递到对应的 handler,该 handler 中可以读取到相应请求的 header 信息,保证了每个请求的独立性。
在创建 Web 服务器的时候,我们通过 ListenAndServe
函数的第二个参数传递了一个 handler,这个 handler 为 nil,在 ServeHTTP 函数中如果 handler 为 nil,会给这个 handler 赋值一个 DefaultServeMux。
err := http.ListenAndServe(":9000", nil)
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
DefaultServeMux 就是一个 ServeMux:
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
ServeMux 结构体的定义如下:
type ServeMux struct {
mu sync.RWMutex // 锁,请求涉及到并发处理,这里需要一个锁的机制
m map[string]muxEntry // 路由规则,一个 string 对应一个 mux 实体,这里的 string 就是注册的路由表达方式
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
muxEntry 结构体的定义如下:
type muxEntry struct {
h Handler // 路由对应的 handler
pattern string
}
Handler 结构体的定义如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
Handler 是一个接口,该接口中定义了一个 ServeHTTP 方法,查看 HandleFun:
HandleFun 类型定义如下:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
我们在创建 Web 服务器的时候通过 HandleFun 函数将路由和处理函数进行了绑定:
http.HandleFunc("/", sayHelloName)
我们定义的方法 sayHelloName 虽然没有直接实现 ServeHTTP 方法,但是 sayHelloName 方法就是 HandleFunc 调用后的结果,这个类型默认实现了 ServeHTTP 方法,即调用了 HandleFunc(f) 强制类型转换 f 为 HandleFunc 类型,这样就拥有了 ServeHTTP 方法。
路由器里面存储好了相应的路由映射规则后,通过调用 mux.handler(r).ServeHTTP(w,r)
来分发请求,*mux.handler(r)
* 方法的源代码如下:
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
}
handler 函数是根据用户请求的 URL 和路由器里面存储的 map 匹配获取存储的 handler,获取之后就可以调用 handler 的 ServeHTTP 方法执行相应的函数了。
Go 支持外部实现路由器,也就是 ListenerAndServe 方法的第二个参数,它是一个 Handler 接口,也就是实现 Handler 接口就可以实现自定义路由器。
在自定义的 Web 服务器的 main.go 文件中增加自定义路由器的代码:
//noinspection ALL
func main(){
//http.HandleFunc("/", sayHelloName)
alphaMux := &AlphaMux{}
err := http.ListenAndServe(":9000", alphaMux)
if err != nil {
log.Fatal("ListenAndServer: ", err)
}
}
func sayHelloName(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "Hello, AlphaMux")
}
// 自定义路由器结果体
type AlphaMux struct {
}
// 自定义路由规则
func (p *AlphaMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayHelloName(w, r)
return
}
http.NotFound(w, r)
return
}
执行 main.go 文件,在浏览器中输入 localhost:9000,浏览器显示内容如下:
自定义的路由器生效。
在使用默认的 DefaultServeMux 作为路由器的代码中,来看一下代码的执行流程:
Go 首先会调用 *http.HandleFunc
*,这一行代码内部执行了以下几步:
第一步:调用了 DefaultServeMux 的 HandleFunc 方法;
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
第二步:调用了 DefaultServeMux 的 Handle 方法:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
第三步:通过 Handler 方法,往 DefaultServeMux 的 map[string]muxEntry 中增加对应的 handler 和路由规划:
接着调用了 http.ListenAndServe("9000", nil)
,这行代码内部执行了以下几件事:
第一:实例化一个 Server,并调用 Server 的 ListenAndServe() 方法
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
第二:ListenAndServe() 方法中通过 net.Listen 监听传递的端口,在 ListenAndServe() 方法最后调用了 Serve() 方法:
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)
}
第三:Serve 方法中通过 for()
循环接收请求,然后在该方法末尾实例化了一个新的连接 Conn,并开启一个新的 goroutine 为这个请求进行服务 go serve(connCtx)
:
func (srv *Server) Serve(l net.Listener) error {
// 其他代码省略
for {
rw, err := l.Accept()
// 其他代码省略
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
第四:serve()
函数中通过 for
循环读取请求,并给每个请求分发一个 handler,分发 handler 是通过 ServeHTTP() 方法完成的:
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
// 其余代码省略
// 通过 for 循环读取请求
for {
w, err := c.readRequest(ctx)
// 其余代码省略
// 获取 handler
serverHandler{c.server}.ServeHTTP(w, w.req)
inFlightResponse = nil
// 其余代码省略
}
}
第五:ServeHTTP 方法会分配 handler,我们在 main.go 文件中的 ListenAndServe 方法传递的第二个参数为 nil
,这里就分配了一个 DefaultServeMux,并在最后调用了 DefaultServeMux 的 ServeHTTP 方法:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
// 其余代码省略
handler.ServeHTTP(rw, req)
}
第六:DefaultServeMux 其实就是一个 ServeMux, ServeMux 的 ServeHTTP 方法会根据请求匹配相应的 handler
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)
}
第七:查看 Handler() 方法的源码,是通过 mux.handler() 方法来选择 handler,handler 的选择会有三种情况:
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
}
第八:最后调用 handler 的 ServeHTTP 方法就实现了响应客户端: