Go HTTP模块处理流程简析

Go语言提供完善的net/http包,用户使用起来非常方便简单,只需几行代码就可以搭建一个简易的Web服务,可以对Web路由、静态文件、cookie等数据进行操作。


一个使用http包建立的Web服务

package main

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

func RequestHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World")
}

func main() {
    http.HandleFunc("/", RequestHandler)

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

核心代码代码如下,下面对它们进行解析

// 用户自定义HTTP路由句柄
func RequestHandler(w http.ResponseWriter, r *http.Request)

// Multiplexer路由注册
http.HandleFunc("/", RequestHandler)

// 服务器监听本地地址,并循环处理HTTP请求
http.ListenAndServe(":8080", nil)

创建ServeMux路由handler

func RequestHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World")
}

这个是用户自定义的函数,结合第二行代码看,作为http.HandleFunc函数调用的第二个参数,因为http.HandleFunc第二个参数类型是func(ResponseWriter, *Request),所以本函数定义带有(ResponseWriter, *Request)参数列表,无返回值。第一个参数http.ResponseWriter是一个接口,用于对一次HTTP请求做响应处理,第二个参数http.Request是一次HTTP请求实例,后面再做详细分析。本函数本质上是一个http请求处理的回调函数,是由http包框架开放出,用户可以自定义处理函数。


ServeMux路由表结构

Go默认路由表结构为ServerMux

// ServeMux结构是一个HTTP请求多路复用器(Multiplexer)
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry   // ServeMux路由表
    hosts bool // whether any patterns contain hostnames
}

// 路由表项
type muxEntry struct {
    h       Handler // 请求处理handler
    pattern string  // 请求路径
}


ServeMux路由注册过程过程

// 往系统默认的ServerMux中添加一条路由
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

// 系统默认的Multiplexer:ServeMux
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    
    // 将用户定义的handler方法,强转为HandlerFunc类型,即实现Handler接口,
    // 后面直接通过f.ServeHTTP(w, r)实现对用户注册的handler的调用,f类型为HandlerFunc
    mux.Handle(pattern, HandlerFunc(handler))
}

// HandlerFunc类型
type HandlerFunc func(ResponseWriter, *Request)

// HandlerFunc类型实现了Handler接口中ServeHTTP方法
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

// 往ServeMux路由表(mux.m,map结构)中添加路由
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern")
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if _, exist := mux.m[pattern]; exist {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    // 向路由表中新增路由表项
    mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

    // 如果不是以'/'开头,则从host开始匹配
    if pattern[0] != '/' {
        mux.hosts = true
    }
}

ServeMux路由匹配过程

// 见net/http/server.go中func (c *conn) serve(ctx context.Context) 1847行(Go SDK 1.11.5)
// 这里就完成对用户注册的路由handler调用
serverHandler{c.server}.ServeHTTP(w, w.req)

// serverHandler类型
type serverHandler struct {
    srv *Server
}

// serverHandler也实现了Handler接口中ServeHTTP方法
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    // http.ListenAndServe(":8080", nil)中第二个参数为nil,表示使用系统默认Multiplexer:DefaultServeMux,
    // 在这里就启用DefaultServeMux,可以查看Server结构对Handler成员注释说明(handler to invoke, http.DefaultServeMux if nil)
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    // 调用ServeMux实现Handler接口中ServeHTTP方法
    handler.ServeHTTP(rw, req)
}

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
    }
    // 通过Request找到路由
    h, _ := mux.Handler(r)

    // 到这里就调用用户注册的路由Handler
    h.ServeHTTP(w, r)
}

// 进一步跟踪mux.Handler(r)
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    // ...
    return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    // 通过路径匹配路由
    if mux.hosts {
        h, pattguizeern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    // Check for exact match first.
    v, ok := mux.m[path]
    if ok {
        return v.h, v.pattern
    }

    // Check for longest valid match.
    var n = 0
    for k, v := range mux.m {
        if !pathMatch(k, path) {
            continue
        }
        // 匹配时选择匹配度最高的路由(长匹配优先于短匹配)
        if h == nil || len(k) > n {
            n = len(k)
            h = v.h
            pattern = v.pattern
        }
    }
    return
}

// 如果pattern不是以'/'结尾,则需完全匹配
// 如果pattern是以'/'结尾,并且pattern(/tree/)是path(/tree/xxx)的前缀子串,
// 则path(/tree/xxx)属于pattern(/tree/*)路由子集下,后面通过match中for语句,基于长匹配优先于短匹配原则,完全匹配路由
func pathMatch(pattern, path string) bool {
    if len(pattern) == 0 {
        // should not happen
        return false
    }
    n := len(pattern)
    if pattern[n-1] != '/' {
        return pattern == path
    }
    return len(path) >= n && path[0:n] == pattern
}


ServeMux路由匹配规则

对于每个HTTP请求,ServeHTTP会对URL进行模式(pattern)匹配,然后调用注册在此pattern下的handler来处理当前请求。

1、如果pattern以'/'开头,表示匹配URL的路径部分,如果不是以'/'开头,表示从host开始匹配;
2、模式匹配时,以匹配度最高为原则(长匹配优先于短匹配);
3、如果pattern(/tree/)以'/'结尾,将会对不带'/'的URL(/tree)进行301重定向到'/tree/'上,除非单独为'/tree'模式注册handler;
4、如果pattern(/tree)注册了handler,当请求的路径为'/tree/'时,则无法匹配该模式;



实现Handler接口

Handler结构定义如下

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

net/http/server.go中实现Handler接口有以下方法

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)}
func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request)
func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request)
func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request)

猜你喜欢

转载自www.cnblogs.com/wayne666/p/10571655.html