天天看点

Go语言HTTP Server源码分析

go语言中http server:

http server,顾名思义,支持http协议的服务器,http是一个简单的请求-响应协议,通常运行在tcp之上。通过客户端发送请求给服务器得到对应的响应。

Go语言HTTP Server源码分析

http服务简单实现

package main  

import ( 

    "fmt" 

    "net/http" 

)  

//③处理请求,返回结果 

func hello(w http.responsewriter, r *http.request) { 

    fmt.fprintln(w, "hello world") 

}  

func main() { 

    //①路由注册 

    http.handlefunc("/", hello)  

    //②服务监听 

    http.listenandserve(":8080", nil) 

Go语言HTTP Server源码分析

你以为这样就结束了吗,不才刚刚开始。

源码分析

①路由注册

func handlefunc(pattern string, handler func(responsewriter, *request)) { 

    defaultservemux.handlefunc(pattern, handler) 

defaultservemux是什么?

defaultservemux是servemux的一个实例。

servemux又是什么?

// defaultservemux is the default servemux used by serve. 

var defaultservemux = &defaultservemux  

var defaultservemux servemux  

type servemux struct { 

    mu    sync.rwmutex 

    m     map[string]muxentry 

    hosts bool  

type muxentry struct { 

    explicit bool 

    h        handler 

    pattern  string 

servemux主要通过map[string]muxentry,来存储了具体的url模式和handler(此handler是实现handler接口的类型)。通过实现handler的servehttp方法,来匹配路由(这一点下面源码会讲到)

很多地方都涉及到了handler,那么handler是什么?

type handler interface { 

    servehttp(responsewriter, *request) 

此接口可以算是http server一个枢纽

func (mux *servemux) handlefunc(pattern string, handler func(responsewriter, *request)) { 

    mux.handle(pattern, handlerfunc(handler)) 

type handlerfunc func(responsewriter, *request)  

func (f handlerfunc) servehttp(w responsewriter, r *request) { 

    f(w, r) 

从代码中可以看出handlerfunc是一个函数类型,并实现了handler接口。当通过调用handlefunc(),把hello强转为handlerfunc类型时,就意味着 hello函数也实现servehttp方法。

servemux的handle方法:

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) 

    //把handler和pattern模式绑定到 

    //map[string]muxentry的map上 

    mux.m[pattern] = muxentry{explicit: true, h: handler, pattern: pattern} 

    if pattern[0] != '/' { 

        mux.hosts = true 

   //这里是绑定静态目录,不作为本片重点。 

    n := len(pattern) 

    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { 

        path := pattern 

        if pattern[0] != '/' { 

            path = pattern[strings.index(pattern, "/"):] 

        } 

        url := &url.url{path: path} 

        mux.m[pattern[0:n-1]] = muxentry{h: redirecthandler(url.string(), statusmovedpermanently), pattern: pattern} 

上面的流程就完成了路由注册。

②服务监听

type server struct { 

    addr         string         

    handler      handler        

    readtimeout  time.duration  

    writetimeout time.duration  

    tlsconfig    *tls.config    

    maxheaderbytes int  

    tlsnextproto map[string]func(*server, *tls.conn, handler)  

    connstate func(net.conn, connstate) 

    errorlog *log.logger 

    disablekeepalives int32        nextprotoonce     sync.once  

    nextprotoerr      error      

func listenandserve(addr string, handler handler) error { 

    server := &server{addr: addr, handler: handler} 

    return server.listenandserve() 

//初始化监听地址addr,同时调用listen方法设置监听。 

//最后将监听的tcp对象传入serve方法: 

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

serve(l net.listener)为每个请求开启goroutine的设计,保证了go的高并发。

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 

    srv.tracklistener(l, true) 

    defer srv.tracklistener(l, false)  

    basectx := context.background() // base is always background, per issue 16220 

    ctx := context.withvalue(basectx, servercontextkey, srv) 

    ctx = context.withvalue(ctx, localaddrcontextkey, l.addr()) 

    //开启循环进行监听 

    for { 

       //通过listener的accept方法用来获取连接数据 

        rw, e := l.accept() 

        if e != nil { 

            select { 

            case <-srv.getdonechan(): 

                return errserverclosed 

            default: 

            } 

            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 

        //通过获得的连接数据,创建newconn连接对象 

        c := srv.newconn(rw) 

                c.setstate(c.rwc, statenew) // before serve can return 

       //开启goroutine发送连接请求 

        go c.serve(ctx) 

serve()为核心,读取对应的连接数据进行分配

func (c *conn) serve(ctx context.context) { 

    c.remoteaddr = c.rwc.remoteaddr().string() 

        //连接关闭相关的处理 

    defer func() { 

        if err := recover(); err != nil && err != erraborthandler { 

            const size = 64 << 10 

            buf := make([]byte, size) 

            buf = buf[:runtime.stack(buf, false)] 

            c.server.logf("http: panic serving %v: %v\n%s", c.remoteaddr, err, buf) 

        if !c.hijacked() { 

            c.close() 

            c.setstate(c.rwc, stateclosed) 

    }()  

    .....  

    ctx, cancelctx := context.withcancel(ctx) 

    c.cancelctx = cancelctx 

    defer cancelctx() 

    c.r = &connreader{conn: c} 

    c.bufr = newbufioreader(c.r) 

    c.bufw = newbufiowritersize(checkconnerrorwriter{c}, 4<<10)  

        //读取客户端的请求 

        w, err := c.readrequest(ctx) 

        if c.r.remain != c.server.initialreadlimitsize() { 

            // if we read any bytes off the wire, we're active. 

            c.setstate(c.rwc, stateactive) 

        }

                ................. 

        //处理网络数据的状态 

        // expect 100 continue support 

        req := w.req 

        if req.expectscontinue() { 

            if req.protoatleast(1, 1) && req.contentlength != 0 { 

                // wrap the body reader with one that replies on the connection 

                req.body = &expectcontinuereader{readcloser: req.body, resp: w} 

        } else if req.header.get("expect") != "" { 

            w.sendexpectationfailed() 

            return 

        }  

        c.curreq.store(w) 

        if requestbodyremains(req.body) { 

            registeronhiteof(req.body, w.conn.r.startbackgroundread) 

        } else { 

            if w.conn.bufr.buffered() > 0 { 

                w.conn.r.closenotifyfrompipelinedrequest() 

            w.conn.r.startbackgroundread() 

        //调用serverhandler{c.server}.servehttp(w, w.req) 

        //方法处理请求 

        serverhandler{c.server}.servehttp(w, w.req) 

        w.cancelctx() 

        if c.hijacked() { 

        w.finishrequest() 

        if !w.shouldreuseconnection() { 

            if w.requestbodylimithit || w.closedrequestbodyearly() { 

                c.closewriteandwait() 

        c.setstate(c.rwc, stateidle) 

        c.curreq.store((*response)(nil)) 

        if !w.conn.server.dokeepalives() { 

        if d := c.server.idletimeout(); d != 0 { 

            c.rwc.setreaddeadline(time.now().add(d)) 

            if _, err := c.bufr.peek(4); err != nil { 

                return 

        c.rwc.setreaddeadline(time.time{}) 

③处理请求,返回结果

serverhandler 主要初始化路由多路复用器。如果server对象没有指定handler,则使用默认的defaultservemux作为路由多路复用器。并调用初始化handler的servehttp方法。

type serverhandler struct { 

    srv *server 

func (sh serverhandler) servehttp(rw responsewriter, req *request) { 

    handler := sh.srv.handler 

        handler = defaultservemux 

    if req.requesturi == "*" && req.method == "options" { 

        handler = globaloptionshandler{} 

    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 

    //匹配注册到路由上的handler函数 

    h, _ := mux.handler(r) 

    //调用handler函数的servehttp方法 

    //即hello函数,然后把数据写到http.responsewriter 

    //对象中返回给客户端。 

    h.servehttp(w, r) 

}

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 { 

        //如 127.0.0.1/hello 

        h, pattern = mux.match(host + path) 

    if h == nil { 

        // 如  /hello 

        h, pattern = mux.match(path) 

        h, pattern = notfoundhandler(), "" 

    return 

func (mux *servemux) match(path string) (h handler, pattern string) { 

    var n = 0 

    for k, v := range mux.m { 

        if !pathmatch(k, path) { 

            continue 

      //通过迭代m寻找出注册路由的patten模式 

      //与实际url匹配的handler函数并返回。 

        if h == nil || len(k) > n { 

            n = len(k) 

            h = v.h 

            pattern = v.pattern 

func pathmatch(pattern, path string) bool { 

    if len(pattern) == 0 { 

        // should not happen 

        return false 

        //如果注册模式与请求uri一样返回true,否则false 

    if pattern[n-1] != '/' { 

        return pattern == path 

        //静态文件匹配 

    return len(path) >= n && path[0:n] == pattern 

将数据写给客户端

//主要代码,通过层层封装才走到这一步 

func (w checkconnerrorwriter) write(p []byte) (n int, err error) { 

    n, err = w.c.rwc.write(p) 

    if err != nil && w.c.werr == nil { 

        w.c.werr = err 

        w.c.cancelctx() 

serverhandler{c.server}.servehttp(w, w.req)当请求结束后,就开始执行连接断开的相关逻辑。

总结

go语言通过一个servemux实现了的路由多路复用器来管理路由。同时提供一个handler接口提供servehttp方法,实现handler接口的函数,可以处理实际request并返回response。

servemux和handler函数的连接桥梁就是handler接口。servemux的servehttp方法实现了寻找注册路由的handler的函数,并调用该handler的servehttp方法。

所以说handler接口是一个重要枢纽。

简单梳理下整个请求响应过程,如下图

Go语言HTTP Server源码分析

作者:佚名

来源:51cto