http.ListenAndServe
go
func ListenAndServe(addr string, handler Handler) error
ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
接下来我们看一下这个函数的主要源码流程。
一、通过ListenAndServe的参数创建Server结构体
go
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处理请求
go
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)
}
ListenAndServe监听TCP网络地址 srv.Addr,然后调用服务来处理传入连接的请求。
第二层首先根据srv中Addr定义了监听信息ln, err := net.Listen("tcp", addr)
,然后把监听对象ln作为srv(Server)对象调用Serve方法的参数。
三、监听器持续监听并Accept请求创建连接,处理连接
go
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) //开启单独协程处理连接
}
}
服务接收监听器上的传入连接,创建一个新的服务协程为每个连接。
Serve方法开启一个for循环,for循环中不断从监听器中接受每一个请求Accept
,然后根据获取到的请求创建新的连接,最后开启一个新的协程服务go c.serve(connCtx)
单独处理这个连接。
四、serve服务处理这个连接
go
func (c *conn) serve(ctx context.Context) {
......
serverHandler{c.server}.ServeHTTP(w, w.req)
......
}
第四层主要流程serve函数中会判断是否为https连接,如果是就升级为https连接。接着创建读写文本,最后通过serverHandler结构体调用相应的ServeHTTP方法处理。
五、ServeHttp具体处理逻辑
go
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)
}
第五层主要流程是如果server结构中自带了相应的Handler对象的话,就调用handler自己实现的ServeHTTP函数,如果没有自带的话,就使用标准库中默认DefaultServeMux作为handler。
六、DefaultServeMux默认处理逻辑
go
//部分源码
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)
}
可以看到DefaultServeMux就是ServeMux数据结构的实例对象,标准库中路由注册的默认数据结构是map
,key是接口的字符串,value是处理器。
第六层的主要流程处理逻辑为先使用request为参数,通过调用ServeMux中Handler方法查询ServeMux对象中的map,得到相应的handler,handler调用ServeHTTP函数处理。
go
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
-
如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
-
如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。下面就是对应部分源码,可以看到,调用的ServeHTTP中又回调了用户自定义的函数。
go
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框架也好,都是先把对应接口请求和处理加入到某个数据结构中,然后监听请求,被调用时,再去这个数据结构中查询再进行处理。