golang-net/http源码分析之http server

说明:此文章为腾讯云机器自动从本人csdn博客搬迁过来。是本人授权操作。

申明:无本人授权,不可转载本文。如有转载,本人保留追究其法律责任的权利。

龚浩华,QQ 29185807,月牙寂 道长

第一时间获取文章,可以关注本人公众号 月牙寂道长 yueyajidaozhang

golang-net/http源码分析之http server

1 简介

先看下net/http库中的例子

创建一个http server,简单的几条语句就可以了。

    http.Handle("/foo", fooHandler)

    http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))

其中的接口部分如下

    type HandlerFunc func(ResponseWriter, *Request)

2 路由

要理解http server那么就应该从路由开始。那么开始看源码

        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
        }

        // NewServeMux allocates and returns a new ServeMux.
        func NewServeMux() *ServeMux { return new(ServeMux) }

        // DefaultServeMux is the default ServeMux used by Serve.
        var DefaultServeMux = &defaultServeMux

        var defaultServeMux ServeMux 

ServerMux则是一个http路由struct。里面的map包含了一个路由hash表。

另外上面的代码中,也形成了一个DefaultServeMux,默认的路由对象。

    type ServeMux
        func NewServeMux() *ServeMux
        func (mux *ServeMux) Handle(pattern string, handler Handler)
        func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
        func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
        func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)

下面一个一个来做函数分析

2.1 Handle

    func (mux *ServeMux) Handle(pattern string, handler Handler)

这个函数为注册路由表函数,功能就是创建muxEntry,然后将其放入到路由map中

    func (mux *ServeMux) Handle(pattern string, handler Handler) {
            mux.mu.Lock()
            defer mux.mu.Unlock()

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

            if mux.m == nil {
                mux.m = make(map[string]muxEntry)
            }

            //主要的地方就是在这里
            mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

            if pattern[0] != '/' {
                mux.hosts = true
            }

            // Helpful behavior:
            // If pattern is /tree/, insert an implicit permanent redirect for /tree.
            // It can be overridden by an explicit registration.
            n := len(pattern)
            if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
                // If pattern contains a host name, strip it and use remaining
                // path for redirect.
                path := pattern
                if pattern[0] != '/' {
                    // In pattern, at least the last character is a '/', so
                    // strings.Index can't be -1.
                    path = pattern[strings.Index(pattern, "/"):]
                }
                url := &url.URL{Path: path}
                mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
            }
        }

2.2 HandleFunc

HandleFunc就是对Handle的封装

    func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
        mux.Handle(pattern, HandlerFunc(handler))
    }

2.3 Handler

Handler则是根据request的访问路径,查找相关的路由表,得到处理函数

        func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
            if r.Method != "CONNECT" {
                if p := cleanPath(r.URL.Path); p != r.URL.Path {
                    _, pattern = mux.handler(r.Host, p)
                    url := *r.URL
                    url.Path = p
                    return RedirectHandler(url.String(), StatusMovedPermanently), pattern
                }
            }

            return mux.handler(r.Host, r.URL.Path)
        }


        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
        }

2.4 ServeHTTP

ServerHttp则是路由的入口

    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    //下面是处理url路径
        if r.RequestURI == "*" {
            if r.ProtoAtLeast(1, 1) {
                w.Header().Set("Connection", "close")
            }
            w.WriteHeader(StatusBadRequest)
            return
        }
        //查找handler
        h, _ := mux.Handler(r)
        //具体的处理
        h.ServeHTTP(w, r)
    }

2.5 DefaultServeMux

默认路由,只是对DefaultServeMux的一层封装

        func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
        func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
                DefaultServeMux.HandleFunc(pattern, handler)
            }

3 Server

入口,创建并监听。

其实是创建了一个Server对象

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

看看server都提供哪些操作

    type Server
        func (srv *Server) ListenAndServe() error
        func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error
        func (srv *Server) Serve(l net.Listener) error
        func (srv *Server) SetKeepAlivesEnabled(v bool)

下面我们跟踪过程

3.1 ListenAndServe

        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)})
        }

其中Listen返回的是一个Listener接口

        func Listen(net, laddr string) (Listener, error)

        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
    }

3.2 Serve

真正的入口处

步骤:Accept–>srv.newConn–>c.serve(ctx)

        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
            }

            // TODO: allow changing base context? can't imagine concrete
            // use cases yet.
            baseCtx := context.Background()
            ctx := context.WithValue(baseCtx, ServerContextKey, srv)
            ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
            for {
            //调用Accept监听
                rw, e := l.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)
            }
        }

4 conn

4.1 newConn

创建了一个conn

        func (srv *Server) newConn(rwc net.Conn) *conn {
            c := &conn{
                server: srv,
                rwc:    rwc,
            }
            if debugServerConnections {
                c.rwc = newLoggingConn("server", c.rwc)
            }
            return c
        }

4.2 serve

conn里读取数据,然后进行处理

    // Serve a new connection.
    func (c *conn) serve(ctx context.Context) {
        c.remoteAddr = c.rwc.RemoteAddr().String()
        ...
        c.r = &connReader{r: c.rwc}
        c.bufr = newBufioReader(c.r)
        c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

        ctx, cancelCtx := context.WithCancel(ctx)
        defer cancelCtx()

        for {
            w, err := c.readRequest(ctx)

            ...
            //真正的处理地方
            serverHandler{c.server}.ServeHTTP(w, w.req)
            w.cancelCtx()
            if c.hijacked() {
                return
            }
            w.finishRequest()
            if !w.shouldReuseConnection() {
                if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                    c.closeWriteAndWait()
                }
                return
            }
            c.setState(c.rwc, StateIdle)
        }
    }

4.3 ServeHTTP

最终的地方。通过寻找路由,找到路由处理函数,然后对请求信息做响应处理。

    type serverHandler struct {
        srv *Server
    }

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        //nil则利用默认的路由
        if handler == nil {
            handler = DefaultServeMux
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        handler.ServeHTTP(rw, req)
    }

5 总结

  • 路由部分,构建map来存储路由处理函数
  • 链接处理部分,构建一个tcp listener,然后accept,构建conn
  • 然后通过conn,查找路由,找到处理函数,进行处理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

spring 3.2.x + struts2 + mybatis 3.x + logback 整合配置

与前面的一篇mybatis 3.2.7 与 spring mvc 3.x、logback整合 相比,只是web层的MVC前端框架,从spring mvc转换成s...

3035
来自专栏hbbliyong

WCF 学习总结2 -- 配置WCF

前面一篇文章《WCF 学习总结1 -- 简单实例》一股脑儿展示了几种WCF部署方式,其中配置文件(App.config/Web.config)都是IDE自动生成...

2707
来自专栏Aloys的开发之路

Linux下安装最新的Eclipse

测试环境: OS:Ubuntu 14.04 Eclipse:Eclipse Kepler (4.3.2) JDK: Ubuntu软件源中提供的Eclipse版本...

1807
来自专栏微信公众号:Java团长

Shiro安全框架入门篇(登录验证实例详解)

Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,...

712
来自专栏恰同学骚年

.NET Core微服务之基于IdentityServer建立授权与验证服务

  要学习IdentityServer,事先得了解一下基于Token的验证体系,这是一个庞大的主题,涉及到Token,OAuth&OpenID,JWT,协议规范...

1246
来自专栏依乐祝

[译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

文章地址: https://www.cnblogs.com/yilezhu/p/9276565.html

541
来自专栏代码世界

WebSocket相关

1204
来自专栏菩提树下的杨过

jboss EAP 6.2+ 通过代码控制JNDI数据源

通过Jboss提供的API,可以操控JBoss,效果跟在管理控制台手动操作完全一样,下面是示例代码: 一、pom.xml添加依赖项 <dependency> ...

1926
来自专栏草根专栏

使用Identity Server 4建立Authorization Server (4)

上一篇讲了使用OpenId Connect进行Authentication. 下面讲 Hybrid Flow和Offline Access 目前我们解决方案里面...

4755
来自专栏大内老A

[原创]谈谈基于Kerberos的Windows Network Authentication - Part II

四、引入Ticket Granting  Service 通过上面的介绍,我们发现Kerberos实际上一个基于Ticket的认证方式。Client想要获取Se...

1869

扫码关注云+社区