http.ListenAndServe
func ListenAndServe(addr string, handler Handler) error
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()
}
Server 定义运行HTTP服务器的参数。Server的零值是一个有效的配置。
第一层相当于封装了一下创建Server结构体的过程,更易于用户使用,创建Server结构体之后,调用Server结构体的ListenAndServe方法。
二、定义监听信息(Listen)调用Serve处理请求
func (srv *Server) ListenAndServe() error {if srv.shuttingDown() {return ErrServerClosed}addr := srv.Addrif addr == "" {addr = ":http"}ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln)
}
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 := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw) //创建新连接c.setState(c.rwc, StateNew, runHooks) // before Serve can returngo c.serve(connCtx) //开启单独协程处理连接}}
服务接收监听器上的传入连接,创建一个新的服务协程为每个连接。
Serve方法开启一个for循环,for循环中不断从监听器中接受每一个请求Accept
,然后根据获取到的请求创建新的连接,最后开启一个新的协程服务go c.serve(connCtx)
单独处理这个连接。
四、serve服务处理这个连接
func (c *conn) serve(ctx context.Context) {......serverHandler{c.server}.ServeHTTP(w, w.req)......
}
第四层主要流程serve函数中会判断是否为https连接,如果是就升级为https连接。接着创建读写文本,最后通过serverHandler结构体调用相应的ServeHTTP方法处理。
五、ServeHttp具体处理逻辑
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerif handler == nil {handler = DefaultServeMux}if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}handler.ServeHTTP(rw, req)
}
第五层主要流程是如果server结构中自带了相应的Handler对象的话,就调用handler自己实现的ServeHTTP函数,如果没有自带的话,就使用标准库中默认DefaultServeMux作为handler。
六、DefaultServeMux默认处理逻辑
//部分源码
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryes []muxEntry hosts bool
}type muxEntry struct {h Handlerpattern string
}var DefaultServeMux = &defaultServeMuxvar 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)
}
可以看到DefaultServeMux就是ServeMux数据结构的实例对象,标准库中路由注册的默认数据结构是map
,key是接口的字符串,value是处理器。
第六层的主要流程处理逻辑为先使用request为参数,通过调用ServeMux中Handler方法查询ServeMux对象中的map,得到相应的handler,handler调用ServeHTTP函数处理。
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
-
如果用户自定义实现了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)
}
总结
以上就是当调用接口时候ListenAndServe的大致流程。而其实不管是Golang本身标准库或者是市面上许多go-web框架也好,都是先把对应接口请求和处理加入到某个数据结构中,然后监听请求,被调用时,再去这个数据结构中查询再进行处理。