实现一个简单的 http 服务
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
fmt.Fprint(rw, "hello word")
})
log.Fatal(http.ListenAndServe(":9777", nil))
}
原理解析:
在main()函数的第一行,我们通过http.HandleFunc定义了路由为”/“的响应函数,这个响应函数,接受传来的Request,并对Response做一定的处理即写入HelloWorld然后直接返回给浏览器。然后便可以直接调用http.ListenAndServe来监听本地的9777端口,便可以直接在浏览器上看到Hello World。
HandleFunc 的实现
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
DefaultServeMux是http包中的全局变量,它的原型是ServeMux这个结构体。
type ServeMux struct {
mu sync.RWMutex //保护m
m map[string]muxEntry //URL:Handler映射表
hosts bool
}
type muxEntry struct {
explicit bool
h Handler
pattern string
}
ServerMux 这个结构体的 HandelFunc
方法:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler)) //又调用了mux.Handle
}
而 mux.Handle 就比较简单了,就是将 func(http.ResponseWriter,*http.Request)转换为 http.Handler 然后放入mux的map中。而http.Handler是一个接口类型,二者是如何转换的?不妨看看 mux.HandleFunc
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
上面这个函数,其实就是一个实现了 http.Handler 接口
的类型,该类型底层基础类型就是 func(ResponseWriter, *Request)
,我们知道在go语言中除了 指针与接口
其他基础类型也是可以定义方法的,标准库定义这个一个类型,为的就是将 普通func(ResponseWriter, *Request)
适配到 http.Handler接口
。
ListenAndServe这个方法
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
源码中可以看到该方法会将传入进来的addr参数和handler送给Server这个结构体,从而新建一个server然后调用这个server的ListenAndServe方法,处理流程大致如下:
server监听到有新链接进来,创建一个goroutine来处理新链接
在goroutine中,将请求和响应分别封装为 http.Request和http.ResponseWriter对象。然后用这两个对象作为函数参数调用 server.Handler.serveHTTP(…), 而server.Handler 即为我们传入的 http.ServeMux 对象,而http.ServeMux对象的serveHTTP方法,我们都没有碰过,里面到底做了什么?
http.ServeMux对象的serveHTTP方法做的事,其实就是根据 http.Request对象中的URL 在自己的map中查找对应的Handler(这个又是我们在step1中添加的),然后执行。
绕了一大圈,简单来说就是 每当有新请求进来,server都会为我们新建一个goroutine,并在其中根据请求URL调用 我们在创建server之前添加的 URL:Handler映射表(通过server中的http.Handler字段混入)中的相应URL的Handler.
net/http包中几个重要的类型:
http.ServeMux
: 建立URL:Handler映射表
http.Server
: 运行HTTP Server
http.Request
: 封装客户端HTTP请求数据
http.ResponseWriter
: 用来构造服务器端HTTP响应数据
http.Handler
: URL处理程序必须实现的接口