Golang:net/http包深入理解
2016 年 7 月 17 日
https://www.jianshu.com/p/16210100d43d
hander函数: 具有 func(w http.ResponseWriter, r *http.Requests)
签名的函数(自定义的处理函数)
handler处理器(函数): 经过 HandlerFunc
结构包装的 handler函数
,它实现了ServeHTTP接口方法的函数。调用handler处理器的ServeHTTP方法时,即调用handler函数本身。
handler对象:实现了Handler接口ServeHTTP方法的结构。
1、基本概念
mux: 多路复用器,也实现了ServeHTTP接口,是一个特殊的处理器,负责将请求转发给对应的路由处理器 Handle: 根据路径,注册一个处理器(是一个func来的) HandleFunc: 处理器函数,对Handle进行一次封装,方便程序员调用(是一个func来的) Handler: 是一个实现了ServerHTTP函数的接口 HandlerFunc: 是func(ResponseWriter, *Request)类型的一个别名 //任何实现http.Handler接口的对象都可作为一个处理器。 type Handler interface { ServeHTTP(ResponseWriter, *Request) } //HandlerFunc实现了Handler接口 type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } //Handle 和 HandleFunc(是两个函数) //源码剖析 func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler)) } //使用对比 mux.HandleFunc("/",helloworld) mux.Handle("/",http.HandlerFunc(helloworld))//强制类型转换 经过HandlerFunc结构包装的handler函数,它实现了ServeHTTP接口方法的函数。 (ServeHTTP方法就是调用handler本身而已,本质上是handler) 调用handler处理器的ServeHTTP方法时,即调用handler函数本身。 //我们自己实现的func(ResponseWriter, *Request)函数没有实现Handler接口,所以要加上这一步转化
2、http包默认的server和mux
//http.HandleFunc调用的是,http包默认的mux //由这个默认的mux去调用mux.Handle func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
3、创建新的server、mux
//使用http.Server 即可创建自定义的server对象: //自定义的serverMux对象也可以传到server对象中。 mux := http.NewServeMux() mux.HandleFunc("/",helloworld) server := &http.Server{ Addr: ":8000", ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second, Handler: mux, } server.ListenAndServe()
4、http工作流程
//源码 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) } //HandlerFunc实现了ServerHTTP接口 type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } server底层一直监听着,当有新的请求来时,mux的ServeHTTP方法通过调用其Handler方法寻找注册到路由上的handler函数(就是自己写的HandleFunc),并调用该函数的ServeHTTP方法,即调用handler函数本身。 //--------------------------------------------------------------- mux的Handler方法对URL简单的处理,然后调用handler方法,后者会创建一个锁,同时调用match方法返回一个handler和pattern。 在match方法中,mux的m字段是map[string]muxEntry图,后者存储了pattern和handler处理器函数,因此通过迭代m寻找出注册路由的patten模式与实际url匹配的handler函数并返回。 返回的结构一直传递到mux的ServeHTTP方法,接下来调用handler函数的ServeHTTP方法,即helloworld函数,然后把response写到http.ResponseWriter对象返回给客户端。 上述函数运行结束即`serverHandler{c.server}.ServeHTTP(w, w.req)`运行结束。 接下来就是对请求处理完毕之后上希望和连接断开的相关逻辑。 至此,Golang中一个完整的http服务介绍完毕,包括注册路由,开启监听,处理连接,路由处理函数。 //----------------------------------------------------------------- //总结 多数的web应用基于HTTP协议,客户端和服务器通过request-response的方式交互。一个server并不可少的两部分莫过于路由注册和连接处理。 Golang通过一个ServeMux实现了的multiplexer路由多路复用器来管理路由。 同时提供一个Handler接口提供ServeHTTP用来实现handler处理其函数,后者可以处理实际request并构造response。 ServeMux和handler处理器函数的连接桥梁就是Handler接口。 ServeMux的ServeHTTP方法实现了寻找注册路由的handler的函数,并调用该handler的ServeHTTP方法。 ServeHTTP方法就是真正处理请求和构造响应的地方。 回顾go的http包实现http服务的流程,可见大师们的编码设计之功力。 学习有利提高自身的代码逻辑组织能力。 更好的学习方式除了阅读,就是实践,接下来,我们将着重讨论来构建http服务。尤其是构建http中间件函数。
5、构造中间件Middleware
所谓中间件,就是连接上下级不同功能的函数或者软件,通常进行一些包裹函数的行为,为被包裹函数提供添加一些功能或行为。
go的http中间件很简单,只要实现一个函数签名为func(http.Handler) http.Handler的函数即可。http.Handler是一个接口,接口方法我们熟悉的为serveHTTP。返回也是一个handler。
//还是返回一个处理器,只不过该处理器被包装进了一些东西 func middlewareHandler(next http.Handler) http.Handler{ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ // 执行handler之前的逻辑 next.ServeHTTP(w, r) // 执行完毕handler后的逻辑 }) } //使用时,一层层的包裹进来即可 func main() { http.Handle("/", loggingHandler(http.HandlerFunc(index))) http.ListenAndServe(":8000", nil) }
6、自定义mux
//任何结构体,只要实现了ServeHTTP方法,这个结构就可以称之为handler对象。ServeMux会使用handler并调用其ServeHTTP方法处理请求并返回响应。 //实现Handler接口 func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) //页面显示"404 page not found" return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!") } func main() { mux := &MyMux{} http.ListenAndServe(":9090", mux) //第二个参数就是Handler接口 } //Go其实支持外部实现的路由器 ListenAndServe的第二个参数就是 //用以配置外部路由器的,它是一个Handler接口,即外部路由器只要实现了Handler接口就可以,我们可以在自己实现 //的路由器的ServHTTP里面实现自定义路由功能