本文首先介绍使用http标准库搭建web服务,共三种方式,然后简析内部实现原理,最后对http的使用做出总结。阅读本文需要简单的go基础知识和web开发相关知识。
1.使用http搭建简单的web服务
1.1 单个handler形式
func main() { server := http.Server{ Addr: "127.0.0.1:8081", Handler: &helloHandler{}, } _ = server.ListenAndServe()}type helloHandler struct{}func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "hello World")}复制代码
- 监听
8081
端口。 - 只有一个
Handler
的实现,所有的请求都由helloHandler
的ServeHTTP
方法处理。访问localhost:8081
、localhost:8081/a
、localhost:8081/a/a
都返回hello World
。 - 显然此方式很简陋无法满足需求。
1.2 多个handler
func main() { server2 := http.Server{ Addr: "127.0.0.1:8082", } http.Handle("/hello", &helloHandler{}) http.Handle("/hi", &hiHandler{}) _ = server2.ListenAndServe()}type helloHandler struct{}func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "hello World")}type hiHandler struct{}func (h *hiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "hi World")}复制代码
- 监听
8082
端口。 - 注册了
/hello
、/hi
两个路由,helloHandler
、hiHandler
的ServeHTTP
方法分别处理。 - 方式一相比较,没有为
Server
指定Handler
属性。而http库为我们默认指定了一个名称为DefaultServeMux
(server.go:2196)的Handler
。 可以自己指定Handler
,如下。
mux := http.NewServeMux() mux.Handle("/hello", &helloHandler{}) mux.Handle("/hi", &hiHandler{}) server2 := http.Server{ Addr: "127.0.0.1:8082", Handler:mux, }复制代码
1.3 HandlerFunc
func main() { server3 := http.Server{ Addr: "127.0.0.1:8083", } http.HandleFunc("/hello", helloFunc) http.HandleFunc("/hi", hiFunc) _ = server3.ListenAndServe()}func helloFunc(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "hello World")}func hiFunc(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "hi World")}复制代码
- 监听
8083
端口。 - 注册了
/hello
、/hi
两个路由,分别由helloFunc
、hiFunc
两个函数处理。 - 同方式二一样,并没有为
Server
指定Handler
属性,由标准库自己指定。可以自己指定Handler
,如下。
mux := http.NewServeMux()mux.HandleFunc("/hello",helloFunc)mux.HandleFunc("/hi",hiFunc) server3 := http.Server{ Addr: "127.0.0.1:8083", Handler:mux,}复制代码
2.http库内部实现简析
2.1 http.Server
http.Server
源码:
type Server struct { Handler Handler // handler to invoke, http.DefaultServeMux if nil ... ...}复制代码
http.Handler
源码:
type Handler interface { ServeHTTP(ResponseWriter, *Request)}复制代码
Server
结构中Handler
接口是真正处理所有
请求的。- 传入自定义的
Handler
必须实现Handler
重写ServeHTTP
方法,例如方式一。 - 当没有指定
Handler
时,http
标准库有一个默认的实现http.DefaultServeMux
。 ServeMux
结构体的ServeHTTP
负责将请求映射到我们注册的handler
,不是直接在方法中处请求。逻辑如下:
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) //根据请求寻找对应的Handler h.ServeHTTP(w, r) //调用我们自己的handler,处理请求}复制代码
ServeMux
主要有两个功能。其一,实现路由注册,方法是func (mux *ServeMux) Handle(pattern string, handler Handler)
和func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
,路由信息存储在map
中。其二实现请求路由,方法是func (mux * ServeMux) ServeHTTP(w ResponseWriter, r *Request)
。
http.ServeMux
源码
type ServeMux struct { m map[string]muxEntry //存储路由信息 ...}复制代码
2.2Handler
与HandlerFunc
比较
- 路由注册时调用方法不同。自定义的
Handler
和HandlerFunc
分别调用http.Handle
和http.HandleFunc
。例如:http.Handle("/hello", &helloHandler{})
和http.HandleFunc("/hello", hiFunc)
。 - 实现
http.Handler
接口的方式不同。自定义的Handler
定义一个签名为ServeHTTP(w ResponseWriter, r *Request)
的方法就行。而自定义HandlerFunc
不仅方法参数与ServeHTTP
一样,并且在内部实现了转换。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { ... mux.Handle(pattern, HandlerFunc(handler))//调用HandlerFunc实现类型转换}复制代码
HandlerFunc
类型
type HandlerFunc func(ResponseWriter, *Request)//实现了`http.Handler`func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}复制代码
3.总结
-
面向接口编程,标准库定义接口
Handler
,并且有自己的内部实现ServeMux
,使用者这也可以自己实现,例如,使用过Spring的同学应该深有体会。接口作为一种规范,既有约束性,又有扩展性。 -
函数式编程,在golang中,函数可以不仅可以作为参数、返回值,还可以作为一种类型,例如
HandlerFunc
。函数类型可以自己有的方法,实现接口,并且将具有相同参数的函数转化为特定类型,例如HandlerFunc(handler)
,将普通函数handler
转化为HandlerFunc
类型。
下一节介绍