Go: 电商 API(下):文档与部署
最后更新:2026-08-26
架构有了,安全有了,现在——让它上线。
从第一行代码到生产就绪:Swagger 文档、优雅关闭、Docker deploy、pprof 调优、Prometheus 监控。Bob 的电商 API 准备迎接真实用户了。
1. 你将学到
- Swagger / OpenAPI 自动生成文档
server.Shutdown优雅关闭- Docker multi-stage build + docker-compose 全栈
- pprof 性能分析端点集成
- Prometheus 指标暴露
- 健康检查端点设计
2. 故事:上线前夜
(1) 痛点:没有文档、没有监控、部署靠手动
Bob 的电商 API 功能完整了,但运营团队拒绝验收:
"运营说'没有 API 文档,前端没法对接。没有健康检查,我们不知道服务是否在运行。没有监控,挂了也不知道。部署还要手动 scp 二进制,太原始了。'"
上线 checklist:
❌ API 文档 → 前端每次都要问"这个接口返回什么字段?"
❌ 优雅关闭 → kill 进程导致正在处理的订单丢失
❌ Docker 部署 → scp 二进制到服务器,手动启动
❌ 性能监控 → 不知道 API 慢在哪里
(2) 本课目标:生产就绪
✅ Swagger 文档 → 前端自助查看
✅ Graceful Shutdown → 信号处理 + 等待连接关闭
✅ Docker Compose → 一条命令启动全栈
✅ pprof + Prometheus → 性能可视化
3. 完整实现
▶ 示例:Graceful Shutdown
⚙️ 前置安装:运行
go get github.com/mattn/go-sqlite3⚠️ 注意:go-sqlite3 需要 CGO,Windows 需安装 gcc(MinGW-w64),macOS/Linux 自带
// cmd/server/main.go
package main
import (
"context"
"database/sql"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"ecommerce/internal/handler"
"ecommerce/internal/repository"
"ecommerce/internal/service"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./ecommerce.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
// 依赖注入
userRepo := repository.NewUserRepository(db)
productRepo := repository.NewProductRepository(db)
orderRepo := repository.NewOrderRepository(db)
userSvc := service.NewUserService(userRepo)
productSvc := service.NewProductService(productRepo)
orderSvc := service.NewOrderService(orderRepo, productRepo, userRepo)
userHandler := handler.NewUserHandler(userSvc)
productHandler := handler.NewProductHandler(productSvc)
orderHandler := handler.NewOrderHandler(orderSvc)
mux := http.NewServeMux()
userHandler.Register(mux)
productHandler.Register(mux)
orderHandler.Register(mux)
// 健康检查端点
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
mux.HandleFunc("GET /ready", func(w http.ResponseWriter, r *http.Request) {
if err := db.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
// 超时设置(防慢连接攻击)
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
}
// Graceful Shutdown
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
sig := <-sigCh
log.Printf("收到信号 %v,正在关闭...", sig)
// 给正在处理的请求最多 30 秒完成
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("强制关闭: %v", err)
}
}()
log.Println("电商 API 启动于 :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
log.Println("服务已安全关闭")
}
▶ 示例:pprof + Prometheus 集成
⚙️ 前置安装:运行
go get github.com/prometheus/client_golang/prometheus和go get github.com/prometheus/client_golang/prometheus/promhttp
package main
import (
"fmt"
"net/http"
"net/http/pprof"
"runtime"
"strconv"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// Prometheus 指标
var (
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests",
},
[]string{"method", "path", "status"},
)
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5},
},
[]string{"method", "path"},
)
activeGoroutines = prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "go_goroutines_active",
Help: "Current number of goroutines",
},
func() float64 {
return float64(runtime.NumGoroutine())
},
)
)
func init() {
prometheus.MustRegister(httpRequestsTotal)
prometheus.MustRegister(httpRequestDuration)
prometheus.MustRegister(activeGoroutines)
}
// Prometheus 中间件
func prometheusMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 包装 ResponseWriter 获取状态码
wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(wrapped, r)
duration := time.Since(start)
httpRequestsTotal.WithLabelValues(
r.Method, r.URL.Path, strconv.Itoa(wrapped.statusCode),
).Inc()
httpRequestDuration.WithLabelValues(
r.Method, r.URL.Path,
).Observe(duration.Seconds())
})
}
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
// 在 main 中注册 pprof 和 Prometheus 端点
func registerMonitoring(mux *http.ServeMux) {
// pprof 端点
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
// Prometheus 指标
mux.Handle("/metrics", promhttp.Handler())
}
func main() {
mux := http.NewServeMux()
registerMonitoring(mux)
fmt.Println("Monitoring server on :8080")
http.ListenAndServe(":8080", prometheusMiddleware(mux))
}
▶ 示例:Docker 部署
# Dockerfile(Multi-stage Build)
FROM golang:1.22-alpine AS builder
WORKDIR /app
# 依赖缓存
COPY go.mod go.sum ./
RUN go mod download
# 源码 + 编译
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# === 运行阶段 ===
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
EXPOSE 6060 # pprof
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
CMD ["/server"]
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "8080:8080"
- "6060:6060" # pprof
environment:
- DB_PATH=/data/ecommerce.db
- GIN_MODE=release
volumes:
- data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
volumes:
data:
prometheus_data:
grafana_data:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'ecommerce-api'
static_configs:
- targets: ['api:8080']
metrics_path: '/metrics'
▶ 示例:Swagger 文档
// 用注释生成 OpenAPI 文档(配合 swag 工具)
// Package handler handles HTTP requests.
//
// 电商 API
//
// Schemes: http
// Host: localhost:8080
// BasePath: /api/v1
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// swagger:meta
package handler
import "ecommerce/pkg/model"
// User 用户信息
// swagger:model
type User struct {
ID int `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
// RegisterUser 用户注册
// swagger:route POST /api/v1/register users registerUser
//
// 注册新用户
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Responses:
// 201: User
// 400: ErrorResponse
// 409: ErrorResponse
func (h *UserHandler) RegisterUser(w http.ResponseWriter, r *http.Request) {
// handler implementation
}
// swagger:parameters registerUser
type RegisterUserParams struct {
// in: body
Body model.RegisterRequest
}
// ErrorResponse 错误响应
// swagger:response
type ErrorResponse struct {
// in: body
Body struct {
Error string `json:"error"`
}
}
# 安装 swag CLI
$ go install github.com/swaggo/swag/cmd/swag@latest
# 生成文档
$ swag init -g cmd/server/main.go
# 访问文档
# http://localhost:8080/swagger/index.html
(4) 上线检查清单
✅ Graceful Shutdown — kill 进程后等待请求完成
✅ Health Check — /health 和 /ready 端点
✅ pprof — /debug/pprof/ 性能分析
✅ Prometheus — /metrics 指标暴露
✅ Swagger — API 文档自动生成
✅ Docker — Multi-stage build,镜像 < 20 MB
✅ docker-compose — 一条命令启动全栈
✅ ReadTimeout / WriteTimeout — 防慢连接攻击
✅ DB 连接池 — MaxOpenConns=25, MaxIdleConns=10
✅ server.Shutdown — SIGINT/SIGTERM 处理
❓ 常见问题
/debug/pprof/ 路径;(3) 只在 staging 环境开启。生产环境建议在需要时临时开启采集 profile。promhttp.Handler() 在 /metrics 端点暴露指标。Prometheus 服务定期 scrape 该端点。Grafana 连接 Prometheus 数据源,创建可视化面板。标准指标:请求数、延迟分布、错误率、goroutine 数、GC 次数。swaggo/swag 工具——在 handler 代码中写特定格式的注释,运行 swag init 生成 docs/ 目录。用 swaggo/http-swagger 将文档挂载到 /swagger/ 端点。注释格式:// swagger:route // swagger:model // swagger:parameters。docker-compose up -d 一条命令启动所有服务。生产环境用 docker stack 或 K8s。📖 小节
- Graceful Shutdown:
server.Shutdown(ctx)+signal.Notify - 超时设置:ReadTimeout / WriteTimeout / IdleTimeout
- pprof:
/debug/pprof/端点(不对外暴露) - Prometheus:
/metrics端点 + 自定义指标 - Swagger:
swaggo/swag注释生成 OpenAPI - Docker:Multi-stage build + HEALTHCHECK
- docker-compose:API + Prometheus + Grafana
- 健康检查:
/health(存活) +/ready(就绪)
📝 作业
-
基础题(难度⭐):为本课的电商 API 添加 Graceful Shutdown 和超时设置。用
curl测试/health和/ready端点。用kill -SIGTERM <pid>测试优雅关闭。 -
进阶题(难度⭐⭐):实现完整的 Docker 化部署。要求:(1) Dockerfile multi-stage build;(2) docker-compose.yml(API + Prometheus + Grafana);(3) HEALTHCHECK;(4) pprof 端点(仅内部端口);(5) 验证
docker-compose up后所有服务正常启动。 -
挑战题(难度⭐⭐⭐):实现 Prometheus 监控 + Grafana 可视化面板。要求:(1) 在 API 中添加 Prometheus 中间件(请求数、延迟、错误率);(2) 添加业务指标(如订单创建数、注册用户数);(3) docker-compose 包含 Prometheus + Grafana;(4) 导入/创建 Grafana dashboard 展示 QPS、P99 延迟、错误率、goroutine 数;(5) 压测后用 Grafana 验证指标正确。
🎉 恭喜!你完成了全部 30 课 Go 教程!从 Hello World 到生产级电商 API,你已经掌握了 Go 后端开发的完整技能栈。