net/http启动服务
示例:启动http服务
在go中启动一个http服务只需寥寥几笔,调用http.HandleFunc注册路由,同时启动http.ListenAndServe启动一个8091的http服务。
import (
"net/http"
)
func main() {
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
http.ListenAndServe(":8091", nil)
}
// sendJsonData 以json格式发送请求
func sendJsonData() {
reqBody, _ := json.Marshal(map[string]string{"key1": "val1", "key2": "val2"})
resp, _ := http.Post(":8091", "application/json", bytes.NewReader(reqBody))
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Printf("resp: %s", respBody)
}
注册Handler逻辑
在构建http服务过程中,server启动涉及核心的数据结构有如下几个:
Server:整个 http 服务端模块被封装在Server类中,Handler是Server中最核心的成员字段;Handler:Handler是一个interface,暴露了方法:ServeHTTP,根据 http 请求Request中的请求路径path映射到对应的handler处理函数,对请求进行处理和响应;ServeMux:是对Handler的具体实现,内部通过一个map维护了从path到handler的映射关系.muxEntry:muxEntry为一个handler单元,内部包含了请求路径path+ 处理函数handler两部分。
在net/http包下声明了一个单例ServeMux,当用户通过方法http.HandleFunc注册handler时,则会将其注册到DefaultServeMux当中:
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
ServeMux.HandleFunc内部会将处理函数handler转为HandlerFunc类型,将其作为Handler interface的实现类注册到ServeMux的路由map当中,实现路由注册的核心逻辑位于ServeMux.Handle方法中:
- 将
path和handler包装成一个muxEntry,以path为key注册到路由map ServeMux.m中; - 响应模糊匹配机制. 对于以 ‘/’ 结尾的
path,根据path长度将muxEntry有序插入到数组ServeMux.es中; ```go type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { // … mux.Handle(pattern, HandlerFunc(handler)) }
<div>
<img src="https://picx.zhimg.com/v2-72f71b4b5ed1978d13a5973eae414e37_r.jpg" width="580"/>
</div>
### 启动Server逻辑
启动入口是`server.go#ListenAndServe` -> `server.go#Server.ListenAndServe` -> 创建监听`net.Listen("tcp", addr)` -> `Server#Serve`方法,其中`Server.Serve`很核心,`for` + `listener.accept`模式.
```go
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
return srv.Serve(ln)
}
func (srv *Server) Serve(l net.Listener) error {
for {
rw, err := l.Accept()
connCtx := ctx
c := srv.newConn(rw)
go c.serve(connCtx)
}
}
// conn.serve 从conn中读取封装到response结构体,根据url找到匹配的Handler,然后执行
func (c *conn) serve(ctx context.Context) {
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
for {
w, err := c.readRequest(ctx)
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
}
}
ServeMux.ServeHTTP、ServeMux.Handler、ServeMux.handler等方法,最终在ServeMux.match方法中,以Request中的path为pattern,在路由字典Server.m中匹配handler,最后调用handler.ServeHTTP方法进行请求的处理和响应。
// ServeMux.ServeHTTP -> ServeMux.Handler -> ServeMux.handler
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
return mux.handler(host, r.URL.Path)
}
// mux.match方法,未命中时,会开启模糊匹配(与当前url匹配的最长路径)
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
h, pattern = mux.match(path)
return
}
Gin
可以看出,在net/http的既定框架下,gin所做的是提供了一个gin.Engine对象作为Handler注入其中,从而实现路由注册/匹配、请求处理链路的优化。
Engine为Gin中构建的HTTP Handler,其实现了net/http包下Handler interface的抽象方法: Handler.ServeHTTP,因此可以作为Handler注入到net/http的Server当中,Engine包含的核心内容包括:
- 路由组
RouterGroup:第(2)部分展开 Context对象池pool:基于sync.Pool实现,作为复用gin.Context实例的缓冲池.gin.Context的内容于本文第5章详解;- 路由树数组
trees:共有9棵路由树,对应于9种http方法. 路由树基于压缩前缀树实现,于本文第4章详解.
注册handler
以http post为例,注册handler方法调用顺序为RouterGroup.POST->RouterGroup.handle,接下来会完成三个步骤:
- 拼接出待注册方法的完整路径
absolutePath - 拼接出代注册方法的完整处理函数链
handlers - 以
absolutePath和handlers组成kv对添加到路由树中 ```go func (group *RouterGroup) POST(relativePath string, handlers …HandlerFunc) IRoutes { return group.handle(http.MethodPost, relativePath, handlers) }
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() } ```
处理请求
Engine.handleHTTPRequest方法核心步骤分为三步:
- 根据
http method取得对应的 methodTree - 根据
path从methodTree中找到对应的handlers链 - 将
handlers链注入到gin.Context中,通过Context.Next方法按照顺序遍历调用handler此处根据path从路由树寻找handlers的逻辑位于root.getValue方法中,和路由树数据结构有关,放在本文第4章详解;