http包提供了HTTP客户端和服务端的实现。golang
package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println("hello") }) log.Fatal(http.ListenAndServe("localhost:8000", nil)) }
咱们先分析下面这个函数web
咱们首先来了解下Handler类型 其定义以下编程
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
也就是说凡是实现了 ServeHTTP(ResponseWriter, *Request)方法的类型都是知足Handler接口的,因此咱们能够用下面的方式来实现一个web服务并发
(上面的说法并不严谨,我会在最后讲述使用http.HandlerFunc()进行类型转换来实现知足Handler接口的方法)tcp
import ( "fmt" "log" "net/http" ) type myHandler struct { } func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/": fmt.Println("hello") default: w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "not found: %s\n", r.URL) } } func main() { handler := myHandler{} log.Fatal(http.ListenAndServe("localhost:8000", handler)) }
可能有人注意到了咱们在最开始写的,最简单的web服务中的http.ListenAndServe("localhost:8000", nil)咱们向第二个参数传递的是nil,并不知足http.Handler接口,这个问题咱们留到下面去解答函数
ServeMux类型是HTTP请求的多路转接器。它会将每个接收的请求的URL与一个注册模式的列表进行匹配,并调用和URL最匹配的模式的处理器。.net
上面的例子中myHandler.ServeHTTP()实际上既实现了路由分配又实现了逻辑处理。在大多数状况下,咱们更但愿不一样的路由交由不一样的方法来处理,因此http包中引入了ServeMux类型来帮助咱们实现路由分配code
type myHandler struct { } func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world") } func main() { handler := myHandler{} mux := http.NewServeMux() mux.Handle("/", handler) log.Fatal(http.ListenAndServe("localhost:8000", mux)) }
type ServeMux struct { mu sync.RWMutex //锁,因为请求涉及到并发处理,所以这里须要一个锁机制 m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式 hosts bool // 是否在任意的规则中带有host信息 } type muxEntry struct { explicit bool // 是否精确匹配 h Handler // 这个路由表达式对应哪一个handler pattern string //匹配字符串 }
能够注意到muxEntry中的h的类型为实现Handler接口结构的对象对象
经过ServeMux能够看到实际的路由规则记录在ServeMux.m 中,这个属性是一个map[string]muxEntry 类型,string记录都是路由,muxEntry里包含实际的handlerblog
经过http.ListenAndServe("localhost:8000", mux)很容易想到ServeMux实现了也实现了Handler接口(拥有ServerHTTP()方法)
因此实现路由解析的原理为
源码入下
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { w.Header().Set("Connection", "close") w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(w, r) } func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { if r.Method != "CONNECT" { if p := cleanPath(r.URL.Path); p != r.URL.Path { _, pattern = mux.handler(r.Host, p) return RedirectHandler(p, StatusMovedPermanently), pattern } } return mux.handler(r.Host, r.URL.Path) } 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 }
经过上面路由解析的原理能够了解:据用户请求的URL和路由器里面存储的map去匹配,当匹配到以后返回存储的handler,调用这个handler的ServeHTTP接口就能够执行到相应的函数了。
而将URL和handler 添加到map中则须要使用func (mux *ServeMux) Handle(pattern string, handler Handler)
看上面用法也能够很容易理解 mux.Handle("/", handler) 函数将URL:"/" 添加到了map的键,将对应的handler 添加到了muxEntry结构中
还记得上面的说法:凡是实现了ServeHTTP(ResponseWriter, *Request) 方法的类型都是知足Handler接口的。但实际开发中咱们会发现大部分handler 并无实现ServeHTTP() 的函数,而是以下的一种方式
type myHandler struct { } func (h myHandler) index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world") } func (h myHandler) News(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "news one") } func main() { h := myHandler{} mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc(h.index)) mux.Handle("/news", http.HandlerFunc(h.News)) log.Fatal(http.ListenAndServe("localhost:8000", mux)) }
http.HandlerFunc(h.index)在这里并非函数调用,而是强制的类型转换
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
即咱们调用了HandlerFunc(f),强制类型转换f成为HandlerFunc类型,这样f就拥有了ServeHTTP方法。
由于像上面http.HandlerFunc()方式是很是广泛的,因此下面两个是等价的
mux.HandleFunc("/", h.index) mux.Handle("/news", http.HandlerFunc(h.News))
注意和上面的http.HandlerFunc()进行区分,前者是实现了Handler接口的类型,可以使用强制类型转换http.HandlerFunc(f) 让f拥有ServeHTTP方法。
后者是为默认的ServeMux:DefaultServeMux注册路由和handler
http.ListenAndServe("localhost:8000", nil)中第二参数为nil时,http.Serve()便使用DefaultServeMux做为处理的handler
经过对http包的分析以后,如今让咱们来梳理一下整个的代码执行过程。 首先调用Http.HandleFunc 按顺序作了几件事:
其次调用http.ListenAndServe(":9090", nil) 按顺序作了几件事情:
实例化Server
调用Server的ListenAndServe()
调用net.Listen("tcp", addr)监听端口
启动一个for循环,在循环体中Accept请求
对每一个请求实例化一个Conn,而且开启一个goroutine为这个请求进行服务go c.serve()
读取每一个请求的内容w, err := c.readRequest()
判断handler是否为空,若是没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
调用handler的ServeHttp
在这个例子中,下面就进入到DefaultServeMux.ServeHttp
根据request选择handler,而且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
选择handler: