这是 Golang 学习系列的收官篇。我们把前面 9 篇的所有知识——结构体、接口、错误处理、Context、Testing、并发模式、sync 包——串起来,从零搭建一个生产级 HTTP 服务。写完这篇,你就能独立写出像样的 Go 后端了。
Q1:用标准库搭一个最小 HTTP 服务需要几行?
A: 真的很少:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package main
import ( "encoding/json" "fmt" "net/http" )
type User struct { ID int `json:"id"` Name string `json:"name"` }
func main() { http.HandleFunc("/health", healthHandler) http.HandleFunc("/api/users", usersHandler)
fmt.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }
func healthHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) }
func usersHandler(w http.ResponseWriter, r *http.Request) { users := []User{ {ID: 1, Name: "Izzy"}, {ID: 2, Name: "AI"}, } json.NewEncoder(w).Encode(users) }
|
1 2 3 4 5 6
| curl http://localhost:8080/health
curl http://localhost:8080/api/users
|
10 行代码跑起来一个 REST API——这就是 Go 的魅力。
Q2:怎么设计中间件(Middleware)?
A: Go 的中间件模式很优雅——函数嵌套函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| type Middleware func(http.Handler) http.Handler
func Logger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() wrapped := &responseWriter{ResponseWriter: w, status: 200} next.ServeHTTP(wrapped, r) fmt.Printf("[%s] %s %s %d %v\n", time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL.Path, wrapped.status, time.Since(start), ) }) }
func CORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") if r.Method == "OPTIONS" { return } next.ServeHTTP(w, r) }) }
func Recovery(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] %v\n%s\n", err, debug.Stack()) http.Error(w, `{"error":"internal server error"}`, http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) }
|
链式组合中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func Chain(middlewares ...Middleware) Middleware { return func(final http.Handler) http.Handler { for i := len(middlewares) - 1; i >= 0; i-- { final = middlewares[i](final) } return final } }
mux := http.NewServeMux() mux.HandleFunc("/api/...", handler)
handler := Chain(Recovery, Logger, CORS)(mux)
http.Server{Handler: handler}.ListenAndServe(":8080")
|
执行顺序:Recovery → Logger → CORS → Handler(从外到内)。
Q3:怎么实现 Graceful Shutdown(优雅关闭)?
A: 这是生产环境必须有的能力:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| func main() { mux := http.NewServeMux() mux.HandleFunc("/health", healthHandler) mux.HandleFunc("/api/users", usersHandler)
handler := Chain(Recovery, Logger, CORS)(mux)
server := &http.Server{ Addr: ":8080", Handler: handler, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 120 * time.Second, }
go func() { fmt.Println("Server starting on :8080") if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server error: %v", err) } }()
quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit fmt.Println("\nShutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel()
if err := server.Shutdown(ctx); err != nil { log.Fatalf("server forced to shutdown: %v", err) }
fmt.Println("Server exited gracefully") }
|
Graceful Shutdown 做了什么?
|
直接 kill |
Graceful Shutdown |
| 正在处理的请求 |
立即断开 |
等待完成(最多 15 秒) |
| 新请求 |
— |
拒绝接受 |
| 连接资源 |
可能泄漏 |
正确释放 |
| 客户端体验 |
收到 connection reset |
正常收到响应 |
Q4:怎么组织项目目录结构?实际项目的布局
A: 推荐的中小型项目布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| my-server/ ├── cmd/ │ └── server/ │ └── main.go # 入口 ├── internal/ │ ├── handler/ # HTTP 处理层 │ │ ├── user.go │ │ └── middleware.go │ ├── service/ # 业务逻辑层 │ │ └── user_service.go │ ├── repository/ # 数据访问层 │ │ └── user_repo.go │ └── model/ # 数据模型 │ └── user.go ├── pkg/ # 可被外部引用的工具包 │ └── response/ │ └── api_error.go ├── configs/ │ └── config.yaml ├── scripts/ │ └── migrate.sql ├── test/ │ └── integration/ ├── go.mod ├── go.sum └── README.md
|
分层职责:
| 层 |
职责 |
依赖 |
| handler |
参数校验、调用 service、返回响应 |
service |
| service |
业务逻辑编排、事务管理 |
repository |
| repository |
SQL 执行、缓存操作 |
model |
| model |
结构体定义,纯数据 |
无 |
Q5:完整示例——把所有知识串起来的 Mini API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| func main() { cfg := loadConfig()
db := connectDB(cfg.DatabaseURL) repo := NewUserRepo(db) svc := NewUserService(repo) h := NewUserHandler(svc)
mux := http.NewServeMux() mux.HandleFunc("/api/v1/users", h.ListUsers) mux.HandleFunc("/api/v1/users/", h.GetUser) mux.HandleFunc("/api/v1/users/create", h.CreateUser)
handler := Chain( Recovery, Logger, CORS, AuthMiddleware(cfg.JWTSecret), )(mux)
runGracefully(handler, cfg.Port) }
func (h *UserHandler) ListUsers(w http.ResponseWriter, r *http.Request) { ctx := r.Context() dbCtx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() users, err := h.svc.GetAll(dbCtx) if err != nil { HandleError(w, r, err) return }
w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(users) }
|
注意看 Context 是怎么流动的:
Request → Handler → Service → Repository → DB Driver
每一层都能响应取消和超时。
Q6:怎么测试这个 HTTP 服务?
A: 用 net/http/httptest,不需要启动真实服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func TestListUsers(t *testing.T) { mockRepo := &MockUserRepo{ Users: []User{{ID: 1, Name: "Izzy"}}, }
svc := NewUserService(mockRepo) h := NewUserHandler(srv)
req := httptest.NewRequest("GET", "/api/v1/users", nil) w := httptest.NewRecorder()
h.ListUsers(w, req)
resp := w.Result() if resp.StatusCode != http.StatusOK { t.Errorf("expected 200, got %d", resp.StatusCode) }
var users []User json.NewDecoder(resp.Body).Decode(&users) if len(users) != 1 || users[0].Name != "Izzy" { t.Errorf("unexpected response body") } }
|
集成测试(带真实 HTTP 层):
1 2 3 4 5 6 7 8 9 10 11 12
| func TestAPI_Integration(t *testing.T) { router := setupRouter() server := httptest.NewServer(router) defer server.Close()
resp, _ := http.Get(server.URL + "/api/v1/users") defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { t.Fatal("expected 200") } }
|
全系列回顾
我们用 10 篇文章覆盖了从零基础到能搭建生产服务的完整路径:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 🏗️ 基础篇 go-01 十问十答 → 变量、类型、流程控制、并发入门 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🎯 OOP 篇 go-02 结构体与方法 → 工厂模式、接收者选择、标签、内存对齐 go-03 接口与鸭子类型 → 隐式接口、nil陷阱、error 本质 go-04 组合优于继承 → 三种组合模式、设计模式 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⚙️ 工程实践篇 go-05 Error 处理 → %w包装、sentinel error、panic 边界 go-06 Context 包 → 超时/取消/传值、传播规则、反模式 go-07 Testing → 表驱动测试、mock、benchmark ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🚀 并发进阶篇 go-08 并发模式 → Pipeline/FanOut-In/Worker Pool/Select go-09 sync 包 → Mutex/RWMutex/WG/Once/Pool/atomic go-10 HTTP 服务实战 → 中间件/Graceful Shutdown/项目架构
|
TODO: 用这篇的模板在你自己的服务器上搭一个 mini API——哪怕只是 CRUD 一个 User 表。亲手敲一遍代码比读十篇文章都管用。遇到问题随时回来聊。