关于go:golang-hethttp包

33次阅读

共计 12830 个字符,预计需要花费 33 分钟才能阅读完成。

net/http

web 示例:

package main

import (
    "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 应用的默认 ServeMux
var DefaultServeMux = &defaultServeMux

var 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 main

import (
    "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.Handler

type 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 高级编程

正文完
 0