2. Go Web服务基础:标准库net/http快速搭建REST API、路由与中间件概念
在微服务压测与故障排查之前,必须掌握Go语言最核心的Web服务构建方式。Go标准库 net/http 提供了完整的HTTP服务器、路由分发与中间件扩展能力,无需依赖任何第三方框架即可搭建生产级REST API。本章将围绕“快速搭建-路由设计-中间件机制”三条主线,建立后续压测与修复的基础认知。
2.1 标准库net/http核心组件
net/http 包围绕三个核心接口构建Web服务:
| 组件 | 类型 | 职责 |
|---|---|---|
| Handler | interface | 定义 ServeHTTP(ResponseWriter, *Request) 方法,处理每个HTTP请求 |
| ServeMux | struct | 默认路由表,根据请求路径匹配对应的Handler |
| Server | struct | 监听端口、TLS配置、超时控制、连接管理 |
关键认知: Go的HTTP处理模型是 goroutine-per-request。每个请求在独立的goroutine中执行,天然支持高并发,这也是压测时性能表现优异的基础。
2.2 快速搭建REST API:三步法
以下代码演示如何使用标准库搭建一个包含 GET 和 POST 方法的用户资源REST API:
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var users = []User{{ID: 1, Name: "Alice"}}
func handleUsers(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
case http.MethodPost:
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
u.ID = len(users) + 1
users = append(users, u)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(u)
default:
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/users", handleUsers)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
log.Fatal(server.ListenAndServe())
}
实践要点:
- 使用
http.Error()返回标准错误状态码,便于压测时识别故障 - 始终设置
Content-Type响应头,避免客户端解析异常 ListenAndServe()是阻塞调用,生产环境建议配合signal.Notify实现优雅关闭
2.3 路由匹配规则与路径模式
Go 1.22 之前的 ServeMux 仅支持固定路径和以 / 结尾的前缀匹配。Go 1.22 引入了增强路由模式,支持方法限定和路径参数:
| 模式 | 示例 | 匹配说明 |
|---|---|---|
| 固定路径 | /api/users |
精确匹配 /api/users |
| 前缀匹配 | /api/ |
匹配所有以 /api/ 开头的路径 |
| 方法+路径 | GET /api/users |
仅匹配GET请求到该路径(Go 1.22+) |
| 路径参数 | /api/users/{id} |
捕获路径段为参数 id(Go 1.22+) |
压测陷阱: 使用前缀匹配
/api/ 时,/api/users 和 /api/users/ 会被视为不同路径。压测脚本中URL末尾的斜杠不一致会导致404错误,这是REST API故障的常见来源。
2.4 中间件概念与链式实现
中间件(Middleware)是包裹 Handler 的装饰器函数,用于在请求处理前后执行横切逻辑。Go标准库通过 func(http.Handler) http.Handler 模式实现中间件链。
典型中间件职责:
- 日志记录:记录请求方法、路径、状态码、耗时
- 恢复处理:捕获panic,返回500错误,防止服务崩溃
- 请求限流:基于令牌桶或计数器控制并发
- 认证鉴权:校验Token或API Key
- 超时控制:通过
context.WithTimeout限制请求处理时间
// 日志中间件示例
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
next.ServeHTTP(w, r)
})
}
// 恢复中间件示例
func recoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic recovered: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
// 链式组合
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/users", handleUsers)
var h http.Handler = mux
h = loggingMiddleware(h)
h = recoveryMiddleware(h)
http.ListenAndServe(":8080", h)
}
中间件执行顺序: 先注册的中间件在外层,后注册的在内层。请求进入时按注册顺序执行,响应返回时逆序执行。压测中若中间件存在性能瓶颈(如日志同步写),会显著影响吞吐量。
2.5 路由与中间件的压测关注点
在后续压测章节中,以下与路由和中间件相关的故障模式将反复出现:
| 故障模式 | 根因 | 压测表现 |
|---|---|---|
| 路由404 | 路径末尾斜杠不匹配、方法未允许 | 请求全部返回404,吞吐量骤降 |
| 中间件panic未恢复 | 缺少recovery中间件或恢复逻辑不完善 | 服务进程崩溃,连接中断 |
| 日志中间件阻塞 | 同步写日志导致IO等待 | 高并发下延迟飙升,超时增加 |
| 路由参数解析错误 | Go版本低于1.22,使用 {id} 语法不兼容 |
部分请求返回500,压测结果不稳定 |
本章小结: 标准库
net/http 提供了完整的REST API构建能力,路由匹配规则和中间件链是后续压测脚本设计和故障注入的基础。建议读者在本地运行上述代码,使用 curl 或 wrk 进行简单验证,为第3章的压测工具选型做好准备。