net/http

web 示例:

package mainimport (    "fmt"    "net/http")func main() {    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {        fmt.Fprintln(w, "Hello World")    })    http.ListenAndServe("localhost:8080", nil)}
  • 进行逻辑解决
func(w http.ResponseWriter, r *http.Request){     dosomeing}
  • 调用HandleFunc()注册处理器函数
  • 调用ListenAndServer()监听localhost:8080并提供服务

HandleFunc()与Handle()实现原理

调用档次

http.HandleFunc()http.Handle()的底层实现都依赖于ServerMux.Handle()http.HandleFunc()本人对ServeMux.Handle()进行了封装,封装为ServeMux.HandleFunc() 并定义了本人的数据结构HandlerFunc该构造是函数类型并且实现了Handler 接口。Mux.Handle() 承受一个类型为Handler的参数,所以调用mux.Handle()必须实现Handler接口。http.HandleFunc()通过本人的数据结构HandlerFunc实现了Handler接口所以调用http.HandleFunc()时不必本人去实现Handler接口,间接向http.HandleFUnc()传入pattern stringfunc(w http.ResponseWriter, r *http.Request){}即可;http.Handle()间接调用Mux.Handle() 所以须要调用者本人去定义一个数据结构并且该数据结构要实现Handler接口,在应用该数据结构前对其进行初始化,而后将pattern string 与该数据结构传入http.Handle()

实现

// Handle registers the handler for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }// HandleFunc registers the handler function for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {    DefaultServeMux.HandleFunc(pattern, handler)}

当咱们调用 http.HandleFunc() 注册处理器时,规范库会应用DefaultServeMux默认的 HTTP 服务器 解决申请。

type ServeMux struct {    mu    sync.RWMutex    m     map[string]muxEntry // 处理器键值对map    es    []muxEntry // slice of entries sorted from longest to shortest.从最长到最短排序的entries切片    hosts bool       // whether any patterns contain hostnames 是否有任何模式蕴含主机名}type muxEntry struct {    h       Handler    pattern string}// /DefaultServeMux 是 Serve 应用的默认 ServeMuxvar DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux

http.HandleFunc() 在底层实现上与http. Handle()一样,都应用Handle办法。不同的是http.HandleFunc()在调用Handle前须要对handler 进行强制转化为HandlerFuncHandlerFunc实现了ServeHTTP办法,而Handle()须要本人实现ServeHTTP办法。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {    if handler == nil {        panic("http: nil handler")    }    // 强制转换 将handler转换为HandlerFunc    mux.Handle(pattern, HandlerFunc(handler))}

HandlerFunc以函数类型func(ResponseWriter, *Request)为底层类型,为HandlerFunc实现了ServeHTTP办法即HandlerFunc实现了Handler接口。Go语言只有某个数据结构实现了接口的办法集就实现了这个接口。

只有你的handler函数签名是:

func (ResponseWriter, *Request)

那么这个handlerhttp.HandlerFunc()就有了统一的函数签名,能够将该handler()函数进行类型转换,转为http.HandlerFunc。而http.HandlerFunc实现了http.Handler这个接口。在http库须要调用你的handler函数来解决http申请时,会调用HandlerFunc()ServeHTTP()函数,可见一个申请的根本调用链是这样的

h = getHandler() => h.ServeHTTP(w, r) => h(w, r)
// The HandlerFunc type is an adapter to allow the use of// ordinary functions as HTTP handlers. If f is a function// with the appropriate signature, HandlerFunc(f) is a// Handler that calls f.type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {    f(w, r)}

HandlerFunc类型只是为了不便注册函数类型的处理器。咱们能够间接申明一个实现了Handler接口的数据结构,而后应用http.Handle()注册该数据结构的示例:

package mainimport (    "fmt"    "net/http")// func main() {//     http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {//         fmt.Fprintln(w, "Hello World")//     })//     http.ListenAndServe("localhost:8080", nil)// }type Hello struct{}func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hello World")}func main() {    // http.Hangle承受两个参数:pattern string, handler Handler    // 第二参数为handler,咱们本人定义一个实现了接口handler的数据结构,在这里须要对该数据结构进行初始化。    // h := Hello{}    // http.Handle("/hello", h)    http.Handle("/hello", Hello{})    http.ListenAndServe("localhost:8080", nil)}

为了辨别,咱们将通过HandleFunc()注册的称为处理函数,通过Handle注册的称为处理器。官网解释为Handle registers the handler for the given pattern in the DefaultServeMux HandleFunc registers the handler function for the given pattern in the DefaultServeMux

Handler响应 HTTP 申请,Handler为处理器函数。

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

Handle办法只承受类型为接口Handler的参数。Handle依据pattern参数在ServeMux(多路复用器)注册处理器函数。ServeMux依据最长匹配准则进行匹配Handlerpattern,所以在注册时须要对pattern进行排序。

// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.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)    }    e := muxEntry{h: handler, pattern: pattern}    mux.m[pattern] = e    if pattern[len(pattern)-1] == '/' {        mux.es = appendSorted(mux.es, e)    }    if pattern[0] != '/' {        mux.hosts = true    }}func appendSorted(es []muxEntry, e muxEntry) []muxEntry {    n := len(es)    i := sort.Search(n, func(i int) bool {        return len(es[i].pattern) < len(e.pattern)    })    if i == n {        return append(es, e)    }    // we now know that i points at where we want to insert    es = append(es, muxEntry{}) // try to grow the slice in place, any entry works.    copy(es[i+1:], es[i:])      // Move shorter entries down    es[i] = e    return es}

ListebAndServe

调用档次

实现

ListenAndServe 监听 TCP 网络地址 addr,而后调用 handler 来解决传入连贯上的申请。 承受的连贯被配置为启用 TCP keep-alives。 handler通常为为 nil,在这种状况下应用 DefaultServeMux。 ListenAndServe 总是返回一个非零谬误。

func ListenAndServe(addr string, handler Handler) error {    server := &Server{Addr: addr, Handler: handler}    return server.ListenAndServe()}

ListenAndServe创立了一个ServerServer定义了运行 HTTP 服务器的参数。Server 的零值是无效配置。咱们能够应用这些字段来调节 Web 服务器的参数。

type Server struct {    // Addr 可选地指定服务器要监听的 TCP 地址,    // 以"host:port"的模式。如果为空,则应用":http" (port 80)    Addr string    // 要调用的handler,如果为 nil,则为 http.DefaultServeMux    Handler Handler     // 通过ServerTLS和ListenAndServerTLS TLSConfig 可选地提供一个 TLS 配置以供应用    TLSConfig *tls.Config    ReadTimeout time.Duration    ReadHeaderTimeout time.Duration    WriteTimeout time.Duration    IdleTimeout time.Duration    MaxHeaderBytes int    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)    ConnState func(net.Conn, ConnState)    ErrorLog *log.Logger    BaseContext func(net.Listener) context.Context    ConnContext func(ctx context.Context, c net.Conn) context.Context    inShutdown atomicBool     disableKeepAlives int32         nextProtoOnce     sync.Once     nextProtoErr      error        mu         sync.Mutex    listeners  map[*net.Listener]struct{}    activeConn map[*conn]struct{}    doneChan   chan struct{}    onShutdown []func()}
//ListenAndServe 侦听 TCP 网络地址 srv.Addr//调用handler解决传入连贯上的申请。//承受的连贯配置为启用TCP keep-alives。//如果 srv.Addr 为空,则应用 ":http"。//ListenAndServe 总是返回一个非零谬误。 //Shutdown 或 Close 后,返回的谬误为 ErrServerClosed。func (srv *Server) ListenAndServe() error {    if srv.shuttingDown() {        return ErrServerClosed    }    addr := srv.Addr    if addr == "" {        addr = ":http"    }    // func net.Listen(network string, address string) (net.Listener, error)    // 监听本地网络地址上的通告。    // network为"tcp","tcp4","tcp6","unix" 或 "unixpacket"。    ln, err := net.Listen("tcp", addr)    if err != nil {        return err    }    // func (srv *Server) Serve(l net.Listener) error    return srv.Serve(ln)}

在该办法中,先调用net.Listen()监听端口,将返回的net.Listener作为参数调用Server.Serve()办法

//Serve 承受 Listener l 上的传入连贯,为每个连贯创立一个新的服务 goroutine。服务 goroutine 读取申请,而后调用 srv.Handler 来回复它们。////仅当侦听器返回 *tls.Conn 连贯并且它们在 TLS 中配置为“h2”时才启用 HTTP/2 反对//Config.NextProtos。////Serve 总是返回一个非 nil 谬误并敞开 l。//Shutdown 或 Close 后,返回的谬误为 ErrServerClosed。func (srv *Server) Serve(l net.Listener) error {    // 未包装的listener    if fn := testHookServerServe; fn != nil {        fn(srv, l) // call hook with unwrapped listener    }    origListener := l    // 幂等    l = &onceCloseListener{Listener: l}    defer l.Close()    if err := srv.setupHTTP2_Serve(); err != nil {        return err    }    if !srv.trackListener(&l, true) {        return ErrServerClosed    }    defer srv.trackListener(&l, false)    baseCtx := context.Background()    if srv.BaseContext != nil {        baseCtx = srv.BaseContext(origListener)        if baseCtx == nil {            panic("BaseContext returned a nil context")        }    }    var tempDelay time.Duration // how long to sleep on accept failure    ctx := context.WithValue(baseCtx, ServerContextKey, srv)    // 应用一个有限的for循环,不停地调用Listener.Accept()办法承受新连贯,开启新 goroutine 解决新连贯    for {        rw, err := l.Accept()        if err != nil {            select {            case <-srv.getDoneChan():                return ErrServerClosed            default:            }            // 指数退却策略            // 如果l.Accept()调用返回谬误,咱们判断该谬误是不是临时性地(ne.Temporary())。如果是临时性谬误,Sleep一小段时间后重试,每产生一次临时性谬误,Sleep的工夫翻倍,最多Sleep 1s            if ne, ok := err.(net.Error); ok && ne.Temporary() {                if tempDelay == 0 {                    tempDelay = 5 * time.Millisecond                } else {                    tempDelay *= 2                }                if max := 1 * time.Second; tempDelay > max {                    tempDelay = max                }                srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)                time.Sleep(tempDelay)                continue            }            return err        }        connCtx := ctx        if cc := srv.ConnContext; cc != nil {            connCtx = cc(connCtx, rw)            if connCtx == nil {                panic("ConnContext returned nil")            }        }        tempDelay = 0        // 取得新连贯后,将其封装成一个conn对象        c := srv.newConn(rw)        c.setState(c.rwc, StateNew, runHooks) // before Serve can return        go c.serve(connCtx)    }}

serve()办法不停地读取客户端发送地申请,创立serverHandler对象调用其ServeHTTP()办法去解决申请,而后做一些清理工作。

//提供新的连贯。func (c *conn) serve(ctx context.Context) {  for {    w, err := c.readRequest(ctx)    serverHandler{c.server}.ServeHTTP(w, w.req)    w.finishRequest()  }}

serverHandler只是一个两头的辅助构造

要害代码

type serverHandler struct {    srv *Server}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {    handler := sh.srv.Handler    handler.ServeHTTP(rw, req)}
//serverHandler 委托给服务器的 Handler 或//DefaultServeMux 并且还解决“OPTIONS *”申请。type serverHandler struct {    srv *Server}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {    // 从Server对象中获取Handler,这个Handler就是调用http.ListenAndServe()时传入的第二个参数    handler := sh.srv.Handler        if handler == nil {        handler = DefaultServeMux    }    if req.RequestURI == "*" && req.Method == "OPTIONS" {        handler = globalOptionsHandler{}    }    if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {        var allowQuerySemicolonsInUse int32        req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {            atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)        }))        defer func() {            if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {                sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")            }        }()    }    handler.ServeHTTP(rw, req)}
//ServeHTTP 将申请分派给其模式与申请 URL 最匹配的处理程序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    }    // mux.Handler(r)通过申请的门路信息查找处理器    h, _ := mux.Handler(r)    // 调用处理器的ServeHTTP()办法解决申请    h.ServeHTTP(w, r)}

Handler()通过URL信息查找handler

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {    // 剥离端口    // stripHostPort 返回 h,不带任何尾随 ":<port>"      host := stripHostPort(r.Host)    return mux.handler(host, r.URL.Path)}

handlerHandler的次要实现:

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 map上查找handler,最长匹配模式。

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.  mux.es contains all patterns    // that end in / sorted from longest to shortest.    // 查看最长的无效匹配。 mux.es 蕴含所有以 /结尾的模式,从最长到最短排序。    // 只有注册了/根门路解决,所有未匹配到的门路最终都会交给/门路解决    // 所以在注册handler时须要对pattern进行排序从最长到最短    for _, e := range mux.es {        // func strings.HasPrefix(s string, prefix string) bool        // HasPrefix 测试字符串 s 是否以prefix结尾。        if strings.HasPrefix(path, e.pattern) {            return e.h, e.pattern        }    }    return nil, ""}

ServeMux

示例

http.HandleFunc()/http.Handle()都是将处理器/函数注册到ServeMux的默认对象DefaultServeMux上。应用DefaultServeMux不可控。

一来Server参数都应用了默认值,二来第三方库也可能应用这个默认对象注册一些解决,容易抵触。更重大的是,咱们在不知情中调用http.ListenAndServe()开启 Web 服务,那么第三方库注册的解决逻辑就能够通过网络拜访到,有极大的安全隐患。所以,除非在示例程序中,否则倡议不要应用默认对象。

咱们能够应用http.NewServeMux()创立一个新的ServeMux对象,而后创立http.Server对象定制参数,用ServeMux对象初始化ServerHandler字段,最初调用Server.ListenAndServe()办法开启 Web 服务:

func main() {  mux := http.NewServeMux()  mux.HandleFunc("/", index)  mux.Handle("/hello", Hello(struct{}))      server := &http.Server{    Addr:         ":8080",    Handler:      mux,    ReadTimeout:  20 * time.Second,    WriteTimeout: 20 * time.Second,  }  server.ListenAndServe()}

实现

NewServeMux()调配并返回一个新的ServeMux

// func new(Type) *Type//新的内置函数分配内存。第一个参数是一个类型, 不是一个值,返回的值是一个指向新的指针 调配了该类型的零值。func NewServeMux() *ServeMux { return new(ServeMux) }

ServeMux实现了ServeHTTP办法即ServeMux实现了Handler,故在设置server时能够将ServeMux当作HAndler传入。Server.Handler = nilDefaultServeMux,Server.Handler = mux时应用调用者定义的多路复用器。

中间件(middleware)

中间件对申请进行一些预处理或后处理。它位于Go web服务器和处理器函数之间。

任何办法实现了ServeHTTP,即是一个非法的http.Handler

应用中间件能够剥离非业务逻辑。

中间件通过函数闭包实现。

中间件通过包装handler再返回一个handler

func hello(wr http.ResponseWriter, r *http.Request) {    wr.Write([]byte("hello"))}func timeMiddleware(next http.Handler) http.Handler {    return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) {        timeStart := time.Now()        // next handler        next.ServeHTTP(wr, r)        timeElapsed := time.Since(timeStart)        logger.Println(timeElapsed)    })}func main() {    http.Handle("/", timeMiddleware(http.HandlerFunc(hello)))    err := http.ListenAndServe(":8080", nil)    ...}
customizedHandler = logger(timeout(ratelimit(helloHandler)))

函数链执行过程中的上下文:

链接中间件

customizedHandler = logger(timeout(ratelimit(helloHandler)))

写法太繁琐,咱们能够进行简化。

r = NewRouter()r.Use(logger)r.Use(timeout)r.Use(ratelimit)r.Add("/", helloHandler)

能够如果应用Use()来减少或删除中间件。

实现
type middleware func(http.Handler) http.Handlertype Router struct {    middlewareChain [] middleware    mux map[string] http.Handler}func NewRouter() *Router{    return &Router{}}// 承受中间件切片func (r *Router) Use(m middleware) {    r.middlewareChain = append(r.middlewareChain, m)}// 运行m1(m2(m3(m4(m5(handler)))))func (r *Router) Add(route string, h http.Handler) {    var mergedHandler = h        // for 循环将从中间件的 Slice中循环遍历中间件,并将其本身包裹在传递给函数的 mergedHandler四周。    for i := len(r.middlewareChain) - 1; i >= 0; i-- {        mergedHandler = r.middlewareChain[i](mergedHandler)    }    r.mux[route] = mergedHandler}
参考:
Go 每日一库之 net/http(根底和中间件)
go高级编程