Go net/http标准库深度使用:从路由到反向代理
文章导语
Go的net/http是构建Web服务的基础。大多数开发者用Gin、Echo等框架,却忽略了标准库本身的能力。实际上,Go 1.22引入的增强路由让标准库已足够应对大多数场景。本文将带你看透net/http的架构设计,用纯标准库构建高性能HTTP服务。
一、HTTP服务的底层架构
go
// net/http的核心类型关系
type Server struct {
Addr string
Handler Handler // 根处理器
TLSConfig *tls.Config
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
每个HTTP连接的处理流程:
go
// 伪代码------连接处理流程
func (srv *Server) Serve(l net.Listener) error {
for {
conn, err := l.Accept()
go srv.newConn(conn).serve() // 每个连接一个goroutine
}
}
func (c *conn) serve() {
for {
req, err := c.readRequest() // 解析HTTP请求
handler, _ := c.server.Handler.ServeHTTP(w, req)
c.writeResponse(resp) // 写入响应
}
}
二、Go 1.22路由增强------游戏规则改变者
2.1 方法路由
go
mux := http.NewServeMux()
// Go 1.22: 方法直接嵌入路径
mux.HandleFunc("GET /users/{id}", getUser)
mux.HandleFunc("POST /users", createUser)
mux.HandleFunc("PUT /users/{id}", updateUser)
mux.HandleFunc("DELETE /users/{id}", deleteUser)
2.2 路径参数
go
mux.HandleFunc("GET /api/v1/users/{id}/orders/{orderID}", func(w http.ResponseWriter, r *http.Request) {
userID := r.PathValue("id")
orderID := r.PathValue("orderID")
fmt.Fprintf(w, "User: %s, Order: %s", userID, orderID)
})
2.3 通配符匹配
go
// 匹配 /files/ 后的所有路径
mux.HandleFunc("GET /files/{path...}", serveFiles)
// 精确匹配优先于通配
mux.HandleFunc("GET /files/{$}", listFiles)
三、中间件模式的优雅实现
go
type Middleware func(http.Handler) http.Handler
// 日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// 恢复中间件
func RecoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
http.Error(w, "Internal Server Error", 500)
log.Printf("panic: %v", err)
}
}()
next.ServeHTTP(w, r)
})
}
// 链式组合
func Chain(h http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}
// 使用
handler := Chain(mux, LoggingMiddleware, RecoveryMiddleware, AuthMiddleware)
http.ListenAndServe(":8080", handler)
四、HTTP客户端的生产配置
go
// 生产级别的HTTP客户端配置
var httpClient = &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
DisableCompression: false,
DisableKeepAlives: false,
ForceAttemptHTTP2: true,
},
}
// 带重试的请求
func DoWithRetry(req *http.Request, maxRetries int) (*http.Response, error) {
var resp *http.Response
var err error
for i := 0; i <= maxRetries; i++ {
resp, err = httpClient.Do(req)
if err == nil && resp.StatusCode < 500 {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
if i < maxRetries {
time.Sleep(time.Duration(i+1) * time.Second)
}
}
return nil, fmt.Errorf("max retries exceeded: %w", err)
}
五、实战:构建一个反向代理
go
func NewReverseProxy(targetURL string) *httputil.ReverseProxy {
target, _ := url.Parse(targetURL)
proxy := httputil.NewSingleHostReverseProxy(target)
// 自定义Director 修改请求
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
req.Header.Set("X-Proxy", "Go-Proxy")
req.Header.Set("X-Forwarded-For", req.RemoteAddr)
}
// 自定义错误处理
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
log.Printf("代理错误: %v", err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
}
// 自定义响应修改
proxy.ModifyResponse = func(resp *http.Response) error {
resp.Header.Set("X-Proxied-By", "Go")
return nil
}
return proxy
}
func main() {
proxy := NewReverseProxy("http://localhost:9090")
http.ListenAndServe(":8080", proxy)
}
六、全文总结
- Go 1.22路由支持方法、路径参数、通配符,可替代轻量框架
- 中间件链式组合实现关注点分离
- http.Transport池化配置直接影响性能
- 每次HTTP请求是一个goroutine,无需手动管理协程池
- 标准库已支持HTTP/2和TLS,生产就绪
七、技术进阶展望
- HTTP/3和QUIC协议的Go实现
- fasthttp与net/http的性能对比
- gRPC-Gateway的HTTP/JSON转换
参考文献
- Go net/http包文档: https://pkg.go.dev/net/http
- Go 1.22 Release Notes - Enhanced routing
- Go Blog - Writing Web Applications
- Mat Ryer - Building APIs in Go
- Go源码 net/http/server.go