Go源码分析——http.ListenAndServe()是如何工作的

Go对web服务器的编写提供了非常好的支持,标准库中提供了net/http包来方便编写服务器。许多教程和书籍在讲到用Go编写web服务器时都会直接教新手用http包写一个最简单的hello world服务器,例子差不多都会像这样:

// 这就是用Go实现的一个最简短的hello world服务器.
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        // 服务器的IP地址和端口信息
	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"  // 如果不指定服务器地址信息,默认以":http"作为地址信息
	}
	ln, err := net.Listen("tcp", addr)    // 这里创建了一个TCP Listener,之后用于接收客户端的连接请求
	if err != nil {
		return err
	}
	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})  // 调用Server.Serve()函数并返回
}
可以看到,Server.ListenAndServe中创建了一个服务器Listener,然后在返回时把它传给了Server.Serve()方法并调用Server.Serve()。


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

func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	var tempDelay time.Duration 
	// 这个循环就是服务器的主循环了,通过传进来的listener接收来自客户端的请求并建立连接,
	// 然后为每一个连接创建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()  // 读取客户端的请求
		// ...
		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


发布了50 篇原创文章 · 获赞 13 · 访问量 21万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览