前端 2025-09-27 · 15 min 阅读

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启动涉及核心的数据结构有如下几个:

在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方法中:

// 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包含的核心内容包括:

注册handler

以http post为例,注册handler方法调用顺序为RouterGroup.POST->RouterGroup.handle,接下来会完成三个步骤:

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方法核心步骤分为三步:

相关扩展阅读:

# 前端