• 【golang】http.ListenAndServe源码解析


    http.ListenAndServe

    func ListenAndServe(addr string, handler Handler) error
    
    • 1

    ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。

    接下来我们看一下这个函数的主要源码流程。

    一、通过ListenAndServe的参数创建Server结构体

    func ListenAndServe(addr string, handler Handler) error {
    	server := &Server{Addr: addr, Handler: handler}
    	return server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4

    Server 定义运行HTTP服务器的参数。Server的零值是一个有效的配置。

    第一层相当于封装了一下创建Server结构体的过程,更易于用户使用,创建Server结构体之后,调用Server结构体的ListenAndServe方法。

    二、定义监听信息(Listen)调用Serve处理请求

    func (srv *Server) ListenAndServe() error {
    	if srv.shuttingDown() {
    		return ErrServerClosed
    	}
    	addr := srv.Addr
    	if addr == "" {
    		addr = ":http"
    	}
    	ln, err := net.Listen("tcp", addr)
    	if err != nil {
    		return err
    	}
    	return srv.Serve(ln)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ListenAndServe监听TCP网络地址 srv.Addr,然后调用服务来处理传入连接的请求。

    第二层首先根据srv中Addr定义了监听信息ln, err := net.Listen("tcp", addr),然后把监听对象ln作为srv(Server)对象调用Serve方法的参数。

    三、监听器持续监听并Accept请求创建连接,处理连接

    func (srv *Server) Serve(l net.Listener) error {
    	......
    		for {
    				rw, err := l.Accept() //接收
    				if err != nil {
    					if srv.shuttingDown() {
    						return ErrServerClosed
    					}
    					if ne, ok := err.(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", err, tempDelay)
    						time.Sleep(tempDelay)
    						continue
    					}
    					return err
    				}
    				connCtx := ctx
    				if cc := srv.ConnContext; cc != nil {
    					connCtx = cc(connCtx, rw)
    					if connCtx == nil {
    						panic("ConnContext returned nil")
    					}
    				}
    				tempDelay = 0
    				c := srv.newConn(rw) //创建新连接
    				c.setState(c.rwc, StateNew, runHooks) // before Serve can return
    				go c.serve(connCtx)  //开启单独协程处理连接
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    服务接收监听器上的传入连接,创建一个新的服务协程为每个连接。

    Serve方法开启一个for循环,for循环中不断从监听器中接受每一个请求Accept,然后根据获取到的请求创建新的连接,最后开启一个新的协程服务go c.serve(connCtx)单独处理这个连接。

    四、serve服务处理这个连接

    func (c *conn) serve(ctx context.Context) {
    	......
    	serverHandler{c.server}.ServeHTTP(w, w.req)
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第四层主要流程serve函数中会判断是否为https连接,如果是就升级为https连接。接着创建读写文本,最后通过serverHandler结构体调用相应的ServeHTTP方法处理。

    五、ServeHttp具体处理逻辑

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    	handler := sh.srv.Handler
    	if handler == nil {
    		handler = DefaultServeMux
    	}
    	if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" {
    		handler = globalOptionsHandler{}
    	}
    
    	handler.ServeHTTP(rw, req)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第五层主要流程是如果server结构中自带了相应的Handler对象的话,就调用handler自己实现的ServeHTTP函数,如果没有自带的话,就使用标准库中默认DefaultServeMux作为handler。

    六、DefaultServeMux默认处理逻辑

    //部分源码
    type ServeMux struct {
    	mu    sync.RWMutex
    	m     map[string]muxEntry
    	es    []muxEntry 
    	hosts bool       
    }
    
    type muxEntry struct {
    	h       Handler
    	pattern string
    }
    
    var DefaultServeMux = &defaultServeMux
    
    var defaultServeMux ServeMux
    
    ......
    
    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    	if r.RequestURI == "*" {
    		if r.ProtoAtLeast(1, 1) {
    			w.Header().Set("Connection", "close")
    		}
    		w.WriteHeader(StatusBadRequest)
    		return
    	}
    	h, _ := mux.Handler(r)
    	h.ServeHTTP(w, r)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    可以看到DefaultServeMux就是ServeMux数据结构的实例对象,标准库中路由注册的默认数据结构是map,key是接口的字符串,value是处理器。

    第六层的主要流程处理逻辑为先使用request为参数,通过调用ServeMux中Handler方法查询ServeMux对象中的map,得到相应的handler,handler调用ServeHTTP函数处理。

    type Handler interface {
    	ServeHTTP(ResponseWriter, *Request)
    }
    
    • 1
    • 2
    • 3
    • 如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。

    • 如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。下面就是对应部分源码,可以看到,调用的ServeHTTP中又回调了用户自定义的函数。

    type HandlerFunc func(ResponseWriter, *Request)
    
    // ServeHTTP calls f(w, r).
    func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    	f(w, r)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    总结

    以上就是当调用接口时候ListenAndServe的大致流程。而其实不管是Golang本身标准库或者是市面上许多go-web框架也好,都是先把对应接口请求和处理加入到某个数据结构中,然后监听请求,被调用时,再去这个数据结构中查询再进行处理。

  • 相关阅读:
    淘宝客系统开发
    Java 8之后的那些新特性(四):网络请求 Java Http Client
    【Java面试】快手面试遭遇滑铁卢:Java中的单例模式如何实现 满分回答快拿走
    如何破解滑动验证码?
    9月编程排行榜新鲜出炉霸榜还得是它~
    OKHttp
    高阶导数习题
    JZ2440笔记:热插拔驱动
    Java学习禁忌
    ASW3410数据手册|ASW3410设计参数|USB3.0高速数据开关切换IC说明书
  • 原文地址:https://blog.csdn.net/m0_53328239/article/details/136556971