导航

Golang在http处理是一个连接一个协程

Posted on 2013-04-26 08:30  蝈蝈俊  阅读(5926)  评论(1编辑  收藏  举报

比如我们经常会有这样的代码开始一个网络端口监听:

err := http.ListenAndServe(listenAddr, nil)

http包中这个函数的实现是:

func ListenAndServe(addr string, handler Handler) error {

    server := &Server{Addr: addr, Handler: handler}

    return server.ListenAndServe()

}

server.ListenAndServe() 函数的实现如下:

func (srv *Server) ListenAndServe() error {

    addr := srv.Addr

    if addr == "" {

        addr = ":http"

    }

    l, e := net.Listen("tcp", addr)

    if e != nil {

        return e

    }

    return srv.Serve(l)

}

srv.Serve( 函数的实现如下,注意看到有网络连接产生后 c, err := srv.newConn(rw) ,我们开了一个协程。

func (srv *Server) Serve(l net.Listener) error {

    defer l.Close()

    var tempDelay time.Duration // how long to sleep on accept failure

    for {

        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

                }

                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)

                time.Sleep(tempDelay)

                continue

            }

            return e

        }

        tempDelay = 0

        if srv.ReadTimeout != 0 {

            rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))

        }

        if srv.WriteTimeout != 0 {

            rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))

        }

        c, err := srv.newConn(rw)

        if err != nil {

            continue

        }

        go c.serve()

    }

    panic("not reached")

}

 

从上面可以看到,每个客户端请求连接,都是开始了一个协程。

每次请求的实体类包含的信息如下:

type conn struct {

    remoteAddr string               // network address of remote side

    server     *Server              // the Server on which the connection arrived

    rwc        net.Conn             // i/o connection

    lr         *io.LimitedReader    // io.LimitReader(rwc)

    buf        *bufio.ReadWriter    // buffered(lr,rwc), reading from bufio->limitReader->rwc

    hijacked   bool                 // connection has been hijacked by handler

    tlsState   *tls.ConnectionState // or nil when not using TLS

    body       []byte

}

 

c.serve( 方法包含整个连接从打开到关闭的整个生命周期,即,一个连接是一个协程。

// Serve a new connection.

func (c *conn) serve() {

    defer func() {

        err := recover()

        if err == nil {

            return

        }

 

        var buf bytes.Buffer

        fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err)

        buf.Write(debug.Stack())

        log.Print(buf.String())

 

        if c.rwc !=nil { // may be nil if connection hijacked

            c.rwc.Close()

        }

    }()

 

    if tlsConn, ok := c.rwc.(*tls.Conn); ok {

        if err := tlsConn.Handshake(); err != nil {

            c.close()

            return

        }

        c.tlsState = new(tls.ConnectionState)

        *c.tlsState = tlsConn.ConnectionState()

    }

 

    for {

        w, err := c.readRequest()

        if err != nil {

            msg := "400 Bad Request"

            if err == errTooLarge {

                // Their HTTP client may or may not be

                // able to read this if we're

                // responding to them and hanging up

                // while they're still writing their

                // request.  Undefined behavior.

                msg = "413 Request Entity Too Large"

            } else if err == io.EOF {

                break // Don't reply

            } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {

                break // Don't reply

            }

            fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg)

            break

        }

 

        // Expect 100 Continue support

        req := w.req

        if req.expectsContinue() {

            if req.ProtoAtLeast(1, 1) {

                // Wrap the Body reader with one that replies on the connection

                req.Body = &expectContinueReader{readCloser: req.Body, resp: w}

            }

            if req.ContentLength == 0 {

                w.Header().Set("Connection", "close")

                w.WriteHeader(StatusBadRequest)

                w.finishRequest()

                break

            }

            req.Header.Del("Expect")

        } else if req.Header.Get("Expect") != "" {

            // TODO(bradfitz): let ServeHTTP handlers handle

            // requests with non-standard expectation[s]? Seems

            // theoretical at best, and doesn't fit into the

            // current ServeHTTP model anyway.  We'd need to

            // make the ResponseWriter an optional

            // "ExpectReplier" interface or something.

            //

            // For now we'll just obey RFC 2616 14.20 which says

            // "If a server receives a request containing an

            // Expect field that includes an expectation-

            // extension that it does not support, it MUST

            // respond with a 417 (Expectation Failed) status."

            w.Header().Set("Connection", "close")

            w.WriteHeader(StatusExpectationFailed)

            w.finishRequest()

            break

        }

 

        handler := c.server.Handler

        if handler == nil {

            handler = DefaultServeMux

        }

 

        // HTTP cannot have multiple simultaneous active requests.[*]

        // Until the server replies to this request, it can't read another,

        // so we might as well run the handler in this goroutine.

        // [*] Not strictly true: HTTP pipelining.  We could let them all process

        // in parallel even if their responses need to be serialized.

        handler.ServeHTTP(w, w.req)

        if c.hijacked {

            return

        }

        w.finishRequest()

        if w.closeAfterReply {

            break

        }

    }

    c.close()

}