• golang的http分析


    首先,要认识一个贯穿始终的接口http.Handler
    type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
    }     

     

    其中,两个参数,一个是表示响应的接口,另一个表示请求。具体方法先忽略:
    type ResponseWriter interface {
    }
    使用时,这个函数指这定地址和对应的handler
    func ListenAndServe(addr string, handler Handler)

     再看下http包内的一个重要函数,Handle,可见,传入的是一个监听的http path,第二个参数是上述的handler.

    func Handle(pattern string, handler Handler)

    看一下如何使用的:

     
    使用接口形式的Handle + ListenAndServe
    type ImpHandler struct {}
     
    func (h ImpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  // 实现方法
        w.Write([]byte("haha"))
    }
     
    func main() {
        http.Handle("/", ImpHandler{})
        http.ListenAndServe(":12345", nil )
    }
    这里,http消息来了应该是在底层直接调用对应的ServeHTTP。具体是怎么调到的,一层层来看。
    首先看下http.Handle做了什么。
     
    func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
     
    可见,这个Handle函数底层封装了一个对象,其实是对此对象DefaultServeMux进行调用。
    这类型如下:
    type ServeMux struct {
        mu    sync.RWMutex
        m     map[string]muxEntry
        hosts bool // whether any patterns contain hostnames
    }
     
    type muxEntry struct {
        explicit bool
        h        Handler
        pattern  string
    }

    可见,http的path和对应的处理handler的关系以muxEntry维护在这个默认的hash表m中。http.Handle传入的两个参数以hash形式保存在内部的全局变量DefaultServeMux中。

    到此,只是在http业务层面上将相关信息保存下,最后在http请求来时的ListenAndServe中,才进行连接的处理。
     
    func ListenAndServe(addr string, handler Handler) error {
        server := &Server{Addr: addr, Handler: handler}
        return server.ListenAndServe()
    }

    同样,ListenAndServe本身只是一个对外接口,内部也有相应对象Server进行封装。前面说过这个方法是处理连接层面的事,那么这个server就是tcp server的一个抽象

    另一方面,这里又传入了一个handler,这是干吗用的?这里传的是nil,后面再看。
     
    func (srv *Server) ListenAndServe() error {
        addr := srv.Addr
        if addr == "" {
            addr = ":http"
        }
        ln, err := net.Listen("tcp", addr)     // 创建监听了
        if err != nil {
            return err
        }
        return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
    }

    可见,这里直接就监听TCP连接了。其中的ln是个Listener接口,代码这样写比较漂亮:

    // Multiple goroutines may invoke methods on a Listener simultaneously.
    type Listener interface {
        // Accept waits for and returns the next connection to the listener.
        Accept() (Conn, error)
     
        // Close closes the listener.
        // Any blocked Accept operations will be unblocked and return errors.
        Close() error
     
        // Addr returns the listener's network address.
        Addr() Addr
    }
     
    // 这里实现得比较好,覆盖了一个Accept方法,在其中加入了keepAlived的选项。其他两个方法仍旧使用原listener的
    type tcpKeepAliveListener struct {
        *net.TCPListener             // 外层可直接调它的方法不需要指定成员
    }
     
    func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
        tc, err := ln.AcceptTCP()
        if err != nil {
            return
        }
        tc.SetKeepAlive(true)
        tc.SetKeepAlivePeriod(3 * time.Minute)
        return tc, nil
    }
    继续看Server的连接监听处理:
     
    func (srv *Server) Serve(l net.Listener) error {
        defer l.Close()
        if fn := testHookServerServe; fn != nil {
            fn(srv, l)
        }
        var tempDelay time.Duration // how long to sleep on accept failure
     
        if err := srv.setupHTTP2_Serve(); err != nil {
            return err
        }
     
      ////////////////skip
        for {
            rw, e := l.Accept()               // 取出一个连接,对应accept
            if e != nil {
                if ne, ok := e.(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", e, tempDelay)
                    time.Sleep(tempDelay)
                    continue
                }
                return e
            }
            tempDelay = 0
            c := srv.newConn(rw)
            c.setState(c.rwc, StateNew) // before Serve can return
            go c.serve(ctx)
        }
    }

    可见,调用Listener的Accept()后,形成一个抽象的连接,再启单独协程去处理它。

    协程内读出对应的数据后,会进行如下调用,此调用将http的业务与底层的tcp连接结合了起来:
     
            serverHandler{c.server}.ServeHTTP(w, w.req)

    看下面,最终回调回去了。

    // serverHandler delegates to either the server's Handler or
    // DefaultServeMux and also handles "OPTIONS *" requests.
    type serverHandler struct {
        srv *Server
    }
     
    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        if handler == nil {
            handler = DefaultServeMux
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        handler.ServeHTTP(rw, req)
    }
     
    // 最终回到最开始注册Handle的地方,进行ServeHTTP的调用     
    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
              }
              h, _ := mux.Handler(r)
              h.ServeHTTP(w, r)
    }
     
    最终调用到了上文的DefaultServeMux中来。
     
    以上是http一的基础的结构,下面是一些衍生的用法。
     
    使用HandleFunc + ListenAndServe
    func main() {
        fmt.Println("Hello.")
        http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("haha2"))
        })
     
        http.ListenAndServe(":12346", nil )
    }

    其中,func可使用闭包也可不用。

    看下面代码:
    // HandleFunc registers the handler function for the given pattern.
    func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
        mux.Handle(pattern, HandlerFunc(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)
    }

    可见,HandlerFunc只是对Handler的封装,下面同样是通过DefaultServeMux来进行。

    这里的重点是以下的写法,用一个函数来实现某个接口,虽然这接口底层仍然是调用函数本身,这样就可以直接用函数和之前的接口匹配:
     
    // 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)
    }

    实际上的效果是,明明只写了一个函数func(ResponseWriter, *Request),但其他代码却可以通过golang的隐式接口方式通过另一个你不知道的函数调用你!这里,不知道的函数就是ServeHTTP

    Handle掌握了,这里的HandleFunc就容易了。
     
    更进一步,ServeMux也是可以使用自定义的值。这时,传入http.ListenAndServe的第二个参数就是这个mux。
    func NewServeMux() *ServeMux { return new(ServeMux) }

    这个ServeMux,本身又是隐式实现了Handler。

    再次回到这里,可见最终是调到了ServerMux这里:
    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        if handler == nil {
            handler = DefaultServeMux
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        handler.ServeHTTP(rw, req)
    }

    总结下:

    http包给外面提供了三个层次的接口,每个层次暴露的东西不一样:
    第一层: 只需要关心处理逻辑,直接以HandleFunc实现;
    第二层: 以Handle实现,这一层,对外额外暴露了一个Handler接口,需要用户关注一个ServeHTTP的函数;底层仍然是通过DefaultMux来实现。
    第三层: 对外暴露了一个ServeMux,处理请求的方法注册到这个ServeMux上,将ServeMux传入。
     
  • 相关阅读:
    html 基本布局介绍
    Hbase1.1.x Java版之批量查删操作
    java命令执行jar文件
    【转】python多版本并存,python3安装pip
    JavaHbase连接代码示例
    Shell执行将脚本里的变量打印到指定日志文件
    Datax将本地文件导入Hbase数据库!!!酷酷酷
    shell关于日期的加减
    python2安装pymongo
    Python从MongoDB中按天读取数据并格式化日志
  • 原文地址:https://www.cnblogs.com/qqmomery/p/6657959.html
Copyright © 2020-2023  润新知