这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记。
http 包
Go 的 http 有两个核心功能:Conn、ServeMux
Conn 的 goroutine
在 http 包中,当与客户端建立 TCP 连接之后,会建立一个协程来单独处理这一次用户的请求:
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
ServeMux
当 c.server.Handler 为空时,则默认获取 handler = DefaultServeMux。这个 DefaullServeMux 是一个路由器,结构如下:
type ServeMux struct {
mu sync.RWMutex // 锁,由于请求涉及到并发处理,因此这里需要一个锁机制
m map[string]muxEntry // 路由规则,一个 string 对应一个 mux 实体,这里的 string 就是注册的路由表达式
hosts bool // 是否在任意的规则中带有 host 信息
}
muxEntry 的结构如下:
type muxEntry struct {
explicit bool // 是否精确匹配
h Handler // 这个路由表达式对应哪个 handler
pattern string // 匹配字符串
}
Handler 是一个接口:
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
但是用户自定义的 Handler 并没有实现 ServeHTTP 方法,所以在 http 包里面还定义了一个类型 HandlerFunc,这个类型实现了 ServeHTTP 方法,也就是 Handler 接口。调用了 http.HandlerFunc 函数之后,我们自定义的 Handler 会被强制类型转换为 HandleFunc 类型,这样就有了 ServeHTTP方法:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?请看下面的代码,默认的路由器实现了 ServeHTTP:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
w.Header().Set("Connection", "close")
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
在路由器收到请求之后,如果是 *,则关闭连接。否则调用 mux.Handler(r) 返回应设置路由的处理 Handler, 然后执行 h.ServeHTTP(w, r)。
mux.Handler(r) 函数实现如下,它根据用户请求的 URL 和路由器里面存储的 map 去匹配的,当匹配到之后返回存储的 handler,调用这个 handler 的 ServeHTTP 接口就可以执行到相应的函数了。:
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method != "CONNECT" {
if p := cleanPath(r.URL.Path); p != r.URL.Path {
_, pattern = mux.handler(r.Host, p)
return RedirectHandler(p, StatusMovedPermanently), pattern
}
}
return mux.handler(r.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
}
自定义路由器
ListenAndServe 函数的第二个参数可以用来配置外部路由器,它是一个 Handler 接口,即即外部路由器只要实现了 Handler 接口就可以。
package main
import (
"fmt"
"log"
"net/http"
)
type myMux struct {
}
func (p myMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayHello(w, r)
return
}
http.NotFound(w, r)
return
}
func sayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, I am Dawn.")
}
func main() {
mux := myMux{}
err := http.ListenAndServe(":9000", mux)
if err != nil {
log.Fatal(err)
}
}