• Go源代码分析——http.ListenAndServe()是怎样工作的


    Go对webserver的编写提供了很好的支持,标准库中提供了net/http包来方便编写server。很多教程和书籍在讲到用Go编写webserver时都会直接教新手用http包写一个最简单的hello worldserver,样例几乎相同都会像这样:

    // 这就是用Go实现的一个最简短的hello worldserver.
    package main
    
    import "net/http"
    
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte(`hello world`))
    	})
    	http.ListenAndServe(":3000", nil) // <-今天讲的就是这个ListenAndServe是怎样工作的
    }
    

    能够看到。代码真的很简短,仅仅须要几行。我们今天要分析的是http.ListenAndServe(),看看这里面究竟都做了些什么。


    首先,http.ListenAndServe用到的全部依赖都在Go源代码中的/src/pkg/net/http/server.go文件里,打开它会发现这页代码很长,有2000+行,我们Ctrl+F直接找我们感兴趣的部分。发如今1770行左右的部分找到了http.ListenAndServe的定义:

    func ListenAndServe(addr string, handler Handler) error {
    	// 创建一个Server结构体,调用该结构体的ListenAndServer方法然后返回
    	server := &Server{Addr: addr, Handler: handler}
    	return server.ListenAndServe()
    }
    从这个函数中就能够看出,调用http.ListenAndServe之后真正起作用的是Server结构体LisntenAndServe方法。给http.ListenAndServe传递的參数仅仅是用来创建一个Server结构体实例,Server结构体的定义例如以下:

    type Server struct {
    	Addr           string        // server的IP地址和port信息
    	Handler        Handler       // 请求处理函数的路由复用器
    	ReadTimeout    time.Duration 
    	WriteTimeout   time.Duration
    	MaxHeaderBytes int       
    	TLSConfig      *tls.Config  
    	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    	ConnState func(net.Conn, ConnState)
    	ErrorLog *log.Logger
    	disableKeepAlives int32 
    }
    假设我们不传详细的參数给http.ListenAndServe,那么它会自己主动以":http"(等价于":80")和DefaulServeMux作为參数来创建Server结构体实例。

    接下来继续看看Server.ListenAndServe里面都做了些什么,1675行左右能够找到定义:

    func (srv *Server) ListenAndServe() error {
    	addr := srv.Addr
    	if addr == "" {
    		addr = ":http"  // 假设不指定server地址信息。默认以":http"作为地址信息
    	}
    	ln, err := net.Listen("tcp", addr)    // 这里创建了一个TCP Listener,之后用于接收client的连接请求
    	if err != nil {
    		return err
    	}
    	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})  // 调用Server.Serve()函数并返回
    }
    能够看到。Server.ListenAndServe中创建了一个serverListener。然后在返回时把它传给了Server.Serve()方法并调用Server.Serve()。


    继续分析Server.Serve,定义的位置在1690行左右:

    func (srv *Server) Serve(l net.Listener) error {
    	defer l.Close()
    	var tempDelay time.Duration 
    	// 这个循环就是server的主循环了,通过传进来的listener接收来自client的请求并建立连接,
    	// 然后为每个连接创建routine运行c.serve()。这个c.serve就是详细的服务处理了
    	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
    				}
    				srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
    				time.Sleep(tempDelay)
    				continue
    			}
    			return e
    		}
    		tempDelay = 0
    		c, err := srv.newConn(rw)
    		if err != nil {
    			continue
    		}
    		c.setState(c.rwc, StateNew) // before Serve can return
    		go c.serve() // <-这里为每个建立的连接创建routine之后进行服务
    	}
    }

    我们能够接着看看这个conn.serve()里面是怎么进行服务的。代码在1090行附近,仅仅看当中的主要部分:

    func (c *conn) serve() {
    	origConn := c.rwc // copy it before it's set nil on Close or Hijack
    
    	// 这里做了一些延迟释放和TLS相关的处理...
    	
    	// 前面的部分都能够忽略,这里才是基本的循环
    	for {
    		w, err := c.readRequest()  // 读取client的请求
    		// ...
    		serverHandler{c.server}.ServeHTTP(w, w.req) //这里对请求进行处理
    		if c.hijacked() {
    			return
    		}
    		w.finishRequest()
    		if w.closeAfterReply {
    			if w.requestBodyLimitHit {
    				c.closeWriteAndWait()
    			}
    			break
    		}
    		c.setState(c.rwc, StateIdle)
    	}
    }


    经过一路的分析,http.ListenAndServe工作的流程就几乎相同明晰了,我们能够总结成一张流程图:





    假设转载请注明出处:http://blog.csdn.net/gophers


  • 相关阅读:
    mysql的cmd窗口查看数据库信息
    常见抓包工具
    图形数据库
    支付宝支撑双十一4200万次/秒的数据库请求峰值的技术实现
    处理tomcat内存溢出问题
    maven将jar包打如本地仓库命令
    fastJson去掉指定字段
    mybatis insert 返回主键
    maven引入源码
    mysql实现主从复制
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/6719657.html
Copyright © 2020-2023  润新知