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


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

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

    }

  • 相关阅读:
    费曼学习法
    Ubuntu修改系统默认编码
    如何在Ubuntu 18.04上安装和使用PostgreSQL
    Bash简介 & Bash是如何处理命令的
    ubuntu环境变量的三种设置方法
    psql 工具详细使用介绍
    使用ubuntu server18.04 搭建odoo12运行环境
    Ubuntu修改时区和更新时间
    Ubuntu18.04修改apt-get源
    对表内数据间隔特定的长度求和
  • 原文地址:https://www.cnblogs.com/ghj1976/p/3043940.html
Copyright © 2020-2023  润新知