GO http包 | 青训营笔记

103 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第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)
    }
}