分享

理解Go/Golang http库的请求解析过程

 菌心说 2022-02-13

http是go自带的web开发库,具有非常强大的web开发功能。本文以一个代码为例讲解请求的解析过程。

如下代码为例

  1. func main() {
  2. http.HandleFunc('/byte', sayByte)
  3. http.ListenAndServe(':8080', nil)
  4. }
  5. func sayByte(writer http.ResponseWriter, request *http.Request) {
  6. writer.Write([]byte(' say byte byte!!'))
  7. }

1. 路由注册

http.HandleFunc('/byte', sayByte)

1.1 此行代码会调用系统默认的ServeMux即DefaultServeMux,DefaultServeMux是http库定义的一个变量。

DefaultServeMux.HandleFunc(pattern, handler)  // serve.go  2380行

1.2 并且利用HandlerFunc将函数sayByte转换成handler,

mux.Handle(pattern, HandlerFunc(handler))  // serve.go 2368行

1.3 真正向DefaultServeMux中注册路由和handler的是ServeMux的handle函数

  1. func (mux *ServeMux) Handle(pattern string, handler Handler) { // serve.go 2342
  2. mux.mu.Lock()
  3. defer mux.mu.Unlock()
  4. if pattern == '' {
  5. panic('http: invalid pattern')
  6. }
  7. if handler == nil {
  8. panic('http: nil handler')
  9. }
  10. if _, exist := mux.m[pattern]; exist {
  11. panic('http: multiple registrations for ' + pattern)
  12. }
  13. if mux.m == nil {
  14. mux.m = make(map[string]muxEntry)
  15. }
  16. mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
  17. if pattern[0] != '/' {
  18. mux.hosts = true
  19. }
  20. }

1.4 查看ServeMux结构可知,路由和handler存储在ServeMux的m属性中,m是一个map

  1. type ServeMux struct { //serve.go 2133
  2. mu sync.RWMutex
  3. m map[string]muxEntry
  4. hosts bool // whether any patterns contain hostnames
  5. }

到此完成DefaultServeMux的初始化,也就是路由与handler的一一对应关系,存储在一个map中,键是路由,值是muxEntry,而由他存储路由与handler。

2.服务开启

http.ListenAndServe(':8080', nil)

2.1 监听端口

ln, err := net.Listen('tcp', addr) //serve.go 2707

2.2 接受请求

rw, e := l.Accept()  //serve.go 2770

2.3 为请求创建一个连接

c := srv.newConn(rw) //serve.go 2793

2.4 开始服务

go c.serve(ctx)  //serve.go 2795

2.5 初始化ServerHandler,并且调用他的ServeHTTP方法

serverHandler{c.server}.ServeHTTP(w, w.req)  // serve.go //1830

2.6 ServeHttp方法会找出服务的一个ServeMux,如果没有用户自己没有初始化一个ServeMux,则会使用DefaultServeMux,也就是之前默认初始化的ServeMux,最后调用ServeMux的serveHTTP方法。

  1. func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { //serve.go 2686
  2. handler := sh.srv.Handler
  3. if handler == nil {
  4. handler = DefaultServeMux
  5. }
  6. if req.RequestURI == '*' && req.Method == 'OPTIONS' {
  7. handler = globalOptionsHandler{}
  8. }
  9. handler.ServeHTTP(rw, req)
  10. }

2.7 在ServeMux的serveHTTP方法中,找到处理函数并调用

  1. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { //serve.go 2328
  2. if r.RequestURI == '*' {
  3. if r.ProtoAtLeast(1, 1) {
  4. w.Header().Set('Connection', 'close')
  5. }
  6. w.WriteHeader(StatusBadRequest)
  7. return
  8. }
  9. h, _ := mux.Handler(r) //根据url在ServeMux中的m属性中找到处理函数,
  10. h.ServeHTTP(w, r) //调用处理函数
  11. }

2.8 寻找处理函数的代码

  1. func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { /serve.go 2309
  2. mux.mu.RLock()
  3. defer mux.mu.RUnlock()
  4. // Host-specific pattern takes precedence over generic ones
  5. if mux.hosts {
  6. h, pattern = mux.match(host + path)
  7. }
  8. if h == nil {
  9. h, pattern = mux.match(path)
  10. }
  11. if h == nil {
  12. h, pattern = NotFoundHandler(), ''
  13. }
  14. return
  15. }
  1. func (mux *ServeMux) match(path string) (h Handler, pattern string) { // serve.go 2197
  2. // Check for exact match first.
  3. v, ok := mux.m[path]
  4. if ok {
  5. return v.h, v.pattern
  6. }
  7. // Check for longest valid match.
  8. var n = 0
  9. for k, v := range mux.m {
  10. if !pathMatch(k, path) {
  11. continue
  12. }
  13. if h == nil || len(k) > n {
  14. n = len(k)
  15. h = v.h
  16. pattern = v.pattern
  17. }
  18. }
  19. return
  20. }

注意:观察到ServeMux的m属性的值是muxEntry,结构如下

  1. type muxEntry struct { //serve.go 2139
  2. h Handler
  3. pattern string
  4. }

此处的handler是一个接口,在如下代码中,我们传入的是函数,最终由HandlerFunc将函数转成Handler。

http.HandleFunc('/byte', sayByte)

我们也可以直接实现Handler ,那么此时代码如下

  1. type MyHandler struct{}
  2. func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  3. fmt.Fprintf(w, 'Hello World!')
  4. }
  5. func main() {
  6. handler := MyHandler{}
  7. http.Handle('/hello',&handler)
  8. http.ListenAndServe(':8080',nil)
  9. }

文章到此为止,介绍了

1.ServeMux的初始化过程

2.web请求处理过程

文中如果有错误还请严厉指出。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多