golang的HTTP包提供了了很强大的功能,开发人员即使不使用框架也可以很方便的进行开发。下面就简单说一下开发web应用时HTTP包都做了哪些工作。
我们在建立一个WEB应用的时候经常会这样使用:
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello"))
})
http.ListenAndServe(":8080",nil)
这样我们访问8080
端口就会打印hello
。
下面从两个介绍HTTP的工作流程:
1.handler注册过程
2.服务器监听过程
3.当我们访问接口时服务器的处理过程。
一 注册过程
注册的代码是:
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello"))
})
看一下http.HandleFunc
函数:
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler) //调用ServeMux的HandleFunc函数
}
这里发现http.HandleFunc调用的是DefaultServeMux的HandleFunc函数,DefaultServeMux是ServeMux类型的变量。下面看看ServeMux。
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry //key是路径
hosts bool
}
type muxEntry struct {
h Handler //通过HandleFunc函数传过来的handler
pattern string //路径
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler)) //调用Handle
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
//....
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
//....
}
这里HandleFunc函数调用Handle函数,然后通过mux.m[pattern] = muxEntry{h: handler, pattern: pattern}把handler存储到muxEntry然后放到ServeMux的map中。
这里可以发现HandleFunc函数中会把handler强制转换为HandlerFunc类型:HandlerFunc(handler)。
来看看HandleFunc类型。
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
HandlerFunc和我们传入的Handler的函数签名是一样的,只是它还有一个ServeHTTP函数,这个函数会调用我们传入的handler。可以看出HandlerFunc只是对我们传入的handler进行了包装并提供ServeHTTP来调用handler
。
这里总结一下注册handler的流程:
1.http.HandlerFunc调用ServeMux. HandleFunc方法。
2.ServeMux. HandleFunc首先对我们传进来的handler强制转换为HandlerFunc类型(目的是提供ServerHTTP接口来执行我们传进来的Handler),然后调用ServeMux. Handle方法。
3.ServeMux. Handle方法会把path和handler存在ServeMux结构的m
字段中(使用map的形式存放)。
二.监听过程
调用http.ListenAndServe函数,看看它的代码
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe() //调用Server类型的同名方法
}
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)})
}
func (srv *Server) Serve(l net.Listener) error {
//....
for {
rw, e := l.Accept() //等待请求
//...
}
}
http.ListenAndServe调用Server.ListenAndServe方法,该方法首先监听端口net.Listen("tcp", addr) ,然后调用了Server.Serve方法来等待请求的到来。
总结一下:
1.http.ListenAndServe
创建Server
类型变量并调用Server.ListenAndServe
方法。
2.Server.ListenAndServe
方法首先会监听TCP
端口并调用Server.Serve
方法来等待和处理请求。
三.处理请求
处理请求的逻辑也是在Server.Serve方法中。
func (srv *Server) Serve(l net.Listener) error {
//....
for {
rw, e := l.Accept() //等待请求
//...
c := srv.newConn(rw) //这里创建了一个conn结构体,它代表一个连接
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
调用conn.serve方法:
func (c *conn) serve(ctx context.Context) {
//...
serverHandler{c.server}.ServeHTTP(w, w.req)
//...
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
handler.ServeHTTP(rw, req)
}
conn.serve调用serverHandler的ServeHTTP方法,这个方法会调用DefaultServeMux(如果我们没有手动设置新的ServerMux的话)的ServeHTTP方法。
到目前为止,我们已经知道实现ServeHTTP的类型有两个:ServerMux和HandlerFunc,也就是他们都实现了下面的Handler接口。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
这样说来ServeMux和HandlerFunc本质上都是用来相应请求的handler。ServerMux的m字段保存了我们注册的多个HandlerFunc。HandlerFunc作为一个Handler接口的实现是我们自己编写的用来处理请求的,那么ServerMux做为Handler接口的实现的实现是用来干什么的呢?这里可以直接给出答案:是用来做路由的。
看一下ServerMux的ServeHTTP函数:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
//...
h, _ := mux.Handler(r) //获取对应的handler
h.ServeHTTP(w, r) //执行handler
}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
//...
return mux.handler(host, r.URL.Path) //不继续深究,有兴趣看源码,这里返回的是一个handler
}
上面代码很简单,mux.handler的实现是调用的mux.match函数对path进行匹配并返回handler的,这里就不深究了。
总结:
1.Handler
接口:
包含ServeHTTP
方法,实现这个接口得有ServeMux
和HandlerFunc
(conn
也实现了,这里不介绍)。
2.ServeMux
结构体:
1.1 它的m
字段用于存储我们注册的handler
1.2 它实现了Handler
接口,它的ServeHTTP
方法提供了路由功能。
3.HandlerFunc
结构体:
它实现了Handler
接口,我们编写的handler
都会被转换为HandlerFunc
类型,它的ServeHTTP
方法会执行我们注册的handler
。
3.Server
结构体:
监听指定端口,当请求到达的时候创建一个conn
对象并执行它的ServeHTTP
方法。
4.conn
结构体:
一个conn
代表一个连接,它的ServeHTTP
方法会调用ServeMux
的ServeHTTP
方法进行路由。