zoukankan      html  css  js  c++  java
  • 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()

    }

  • 相关阅读:
    自定义View的ToolBar布局报错Error:(2) No resource identifier found for attribute 'context' in package 'c
    在学git之主分支 branch
    获取发布版SHA1
    关于开启线程与UI的操作
    播放音频和视频(VideoView控件)
    通知栏Notification的应用
    Android 真机调式 Installation failed with message 远程主机强迫关闭了一个现有的连接。. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing. WA
    运行程序申请危险权限
    mysql乐观锁总结和实践
    Nginx配置文件nginx.conf中文详解
  • 原文地址:https://www.cnblogs.com/ghj1976/p/3043940.html
Copyright © 2011-2022 走看看