从ListenAndServe了解http请求的处理—— HandleFunc
Posted Go开发笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从ListenAndServe了解http请求的处理—— HandleFunc相关的知识,希望对你有一定的参考价值。
前言
我们在直接使用go http package搭建的http服务时,总会使用类似如下的代码:
func main() {
http.HandleFunc("/hello", HelloServer)
http.ListenAndServe(":12345", nil)
}
但你知道这些代码背后是以什么的逻辑运行的吗?
http.HandleFunc声明的HelloServer是怎么被系统调用到的呢?
为何http.ListenAndServe的handler参数可以设置为nil呢?
今天我们就从http.ListenAndServe入口查看源码背后的逻辑。
为避免文章大片的代码影响可读性,以下分析到的源码只会保留重要部分,其余部分省略。
一、http.ListenAndServe
1.http.ListenAndServe
(1)ListenAndServe
ListenAndServe是整个服务运行的总入口,我们从此出发。
代码如下:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
此func构造了Server,并调用了server的ListenAndServe继续处理。
看下func上方的注释及示例,注释中这一句比较重要:
// Handler is typically nil, in which case the DefaultServeMux is used.
Handler通常是nil,此时,会默认使用DefaultServeMux。
这也是我们示例中为何Handler为nil的原因,至于DefaultServeMux很重要,不过我们放到第二部分再讲。
(2)Server
Server的结构如下:
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke http.DefaultServeMux if nil
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
...
此处再次提到Handler为nil时会使用DefaultServeMux,由此可见DefaultServeMux的重要性。至于,何时使用DefaultServeMux,看后面的源码分析DefaultServeMux。
2.server.ListenAndServe
监听tcp端口
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
此段代码主要是tcp端口的监听Listener的获取及转为tcpKeepAliveListener后继续执行。
3.srv.Serve
新建连接,并发处理每个连接。
...
for {
...
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
...
此处有个无限循环来处理各个连接,并最终新启协程来处理每个连接,这也就是服务端可以同时处理多请求的缘由。
我们重点看:c := srv.newConn(rw)
// Create new connection from rwc.
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}
通过此func,将server及连接net.Conn传入*conn,意味着针对每一连接,srv都会新建一个conn来处理,随后开启新的goroutine并发处理。
4.srv.Serve
(1)Serve
处理具体的请求。
这部分最重要的就一句
func (c *conn) serve(ctx context.Context) {
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
就是这一句负责路由的最终分发处理。
(2)server.Handler
查询对应pattern的HandleFunc
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux //此处重点
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
此func中sh.srv即为最开始提到的Server,sh.srv.Handler即为http.ListenAndServe中传入的handler。同时,此方法中还说明了之前反复提到的问题,即
(1)当传入的handler为nil时,将默认使用DefaultServeMux,这就是问题的根本原因。
(2)当传入的handler不为nil时,将直接交由我们自定义的Handler直接处理。那我们如何自定义Handler呢?
mux := http.NewServeMux()
mux.HandleFunc("/hello", HelloServer)
http.ListenAndServe(":12345", mux)
(3)通过(2)中自定义的Handler,我们可以看出,自定义Handler显然比系统自动构建的要麻烦,同时还增加了代码及逻辑,调用更内部的ServeMux,明显增加了学习成本。因此,系统建议我们设置为nil。
因此,为了了解更多的内容,我们还需要看DefaultServeMux的实现了。
二、http.HandleFunc与http.DefaultServeMux
1.HandleFunc
调用http.Func后发生了什么?
(1)http.HandleFunc
交由DefaultServeMux处理。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
(2)DefaultServeMux.HandleFunc
mux进一步处理pattern及handler,此处也对我们的handler转换为HandleFunc,为最后调用ServerHTTP做准备。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
(3)mux.Handle
存储pattern及handler信息到mux中
func (mux *ServeMux) Handle(pattern string, handler Handler) {
...
mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
...
}
由此可以看到我们自定义的handler转为HandlerFunc后,根据pattern的不同,然后存储在mux的m中,m格式是map[string]muxEntry,里面存储着pattern对应的muxEntry,而muxEntry中存储着pattern及Handler。
type muxEntry struct {
explicit bool
h Handler
pattern string
}
2.DefaultServeMux
(1)DefaultServeMux实际是类型是ServeMux
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
ServeMux针对ServeHTTP的实现如下:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
...
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
(2)mux.Handler
此处针对r,mux的Handler处理如下,主要是针对重定向和一般的处理,此处我们只讨论一般的处理方式。
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
...
return mux.handler(r.Host, r.URL.Path)
}
(3)mux.handler
mux.handler则是获取host和path组成的pattern对应的Handler。
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
(4)mux.match
mux.match则是根据host和path的来匹配pattern和Handler。
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h //此处即为muxEntry中的Handler
pattern = v.pattern
}
}
return
}
里面最重要的一句:
h = v.h
此处v.h
即为muxEntry中的Handler,根据之前我们关于muxEntry相关的了解知道,muxEntry中的h即为我们在http.HandleFunc中传入的自定义HandleFunc。
(5)HandlerFunc ServeHTTP
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
如示例中:
http.HandleFunc("/hello", HelloServer)
当我们请求/hello时,即可在mux.m根据/hello此key即可获取对应的HandleFunc HelloServer。
三、总结
最后,我们再看下前言中提到的两个问题,其实是一个问题:
http.HandleFunc将pattern及我们自定义的handler存储在DefaultServeMux的一个map中。
当http.ListenAndServe的handler为nil时,系统会从DefaultServeMux存储信息的map中匹配pattern获取对应的handler,进而处理连接请求。
下图是一个简易的思维导图:
以上是关于从ListenAndServe了解http请求的处理—— HandleFunc的主要内容,如果未能解决你的问题,请参考以下文章