6.5 KiB
6.5 KiB
P0: 生产可用性特性设计
日期: 2026-06-11 状态: Approved
概述
5 个独立特性,使 lolly 达到生产可部署状态。所有特性互不依赖,可完全并行开发。
1. CORS 中间件
配置
在 security.cors 下新增 CORSConfig 结构体,server 级别配置:
security:
cors:
enabled: true
allowed_origins: ["https://example.com", "https://api.example.com"]
allowed_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers: ["Content-Type", "Authorization", "X-Request-ID"]
expose_headers: ["X-Total-Count"]
allow_credentials: true
max_age: 3600
字段说明:
enabled: 启用开关(默认 false)allowed_origins: 允许的源列表,支持"*"通配(与allow_credentials: true互斥,遵循 CORS 规范)allowed_methods: 允许的 HTTP 方法allowed_headers: 允许的请求头expose_headers: 允许前端读取的响应头allow_credentials: 是否允许发送 Cookiemax_age: preflight 缓存时间(秒),默认 0
文件
| 文件 | 操作 |
|---|---|
internal/middleware/cors/cors.go |
新建:CORS 中间件实现 |
internal/middleware/cors/cors_test.go |
新建:单元测试 |
internal/config/security_config.go |
修改:添加 CORSConfig 结构体 |
internal/config/defaults.go |
修改:CORS 默认值 |
internal/config/validate.go |
修改:CORS 验证(origins+credentials 互斥检查) |
internal/server/middleware_builder.go |
修改:注册 CORS 中间件(步骤 7.5,SecurityHeaders 之后、ErrorIntercept 之前) |
行为
- 非 CORS 请求(无
Origin头):直接 pass-through - Preflight (OPTIONS):返回 204 + CORS 头,不进入后续 handler
- 实际请求:调用
next(ctx)后添加 CORS 响应头 - Origin 不匹配:不加 CORS 头,浏览器阻止跨域
2. Request-ID 传播
配置
无需额外配置,默认启用。
文件
| 文件 | 操作 |
|---|---|
internal/middleware/requestid/requestid.go |
新建:Request-ID 中间件 |
internal/middleware/requestid/requestid_test.go |
新建:单元测试 |
internal/proxy/headers.go |
修改:SetForwardedHeaders 添加 X-Request-ID 传播 |
internal/server/middleware_builder.go |
修改:注册为第一个中间件(AccessLog 之前) |
行为
- 检查入站
X-Request-ID请求头 - 有值 → 复用(信任下游),存入
ctx.SetUserValue("request_id", id) - 无值 → 生成 UUID v4,存入
ctx.SetUserValue - 始终在响应中设置
X-Request-ID头 - 代理转发时,
SetForwardedHeaders自动传播X-Request-ID(从ctx.UserValue("request_id")读取)
与现有代码的关系
internal/variable/builtin.go 已有 $request_id 变量,会从 ctx.UserValue("request_id") 读取。中间件在请求早期设置此值后,变量系统、access log、proxy header forwarding 都能正确使用。
3. /healthz + /readyz 端点
配置
在 monitoring 下新增:
monitoring:
healthz:
enabled: true
path: "/healthz"
readyz:
enabled: true
path: "/readyz"
字段说明:
enabled: 默认 true(开箱即用)path: 可自定义路径
文件
| 文件 | 操作 |
|---|---|
internal/server/healthz.go |
新建:healthz/readyz handler |
internal/server/healthz_test.go |
新建:单元测试 |
internal/config/monitoring_config.go |
修改:添加 HealthzConfig、ReadyzConfig |
internal/config/defaults.go |
修改:默认值 |
internal/server/server.go |
修改:注册端点(三种模式都需要) |
行为
healthz(存活探针):
- GET → 200
{"status":"ok"} - 无任何依赖检查,只要进程活着就返回 200
readyz(就绪探针):
- GET → 200
{"status":"ready"}或 503{"status":"not ready","reasons":["no healthy upstreams"]} - 检查条件:至少有一个 server 已启动 + 至少有一个 upstream 目标可用(如果有配置 proxy 的话)
- 无 proxy 配置的纯静态文件服务器永远返回 200
注册位置
与 status/pprof 同级:
- Single 模式:
locationEngine.AddExact - VHost/Multi 模式:
router.GET
默认启用,不需要 IP 白名单(K8s 探针来自 kubelet)。
4. 环境变量插值
语法
仅 ${ENV_VAR} 花括号语法,与 lolly 自身 $variable 系统无歧义。
缺失环境变量时保留原样(${MISSING_VAR} 不展开)。
文件
| 文件 | 操作 |
|---|---|
internal/config/env.go |
新建:ExpandEnv(data []byte) []byte 函数 |
internal/config/env_test.go |
新建:单元测试 |
internal/config/config.go |
修改:Load() 和 processIncludes() 中调用 ExpandEnv |
实现
正则 \$\{([^}]+)\} 匹配:
- 匹配到 →
os.Getenv(key) - 环境变量存在 → 替换为值
- 环境变量不存在 → 保留
${key}原样
调用位置:
Load():os.ReadFile之后、yaml.Unmarshal之前processIncludes(): 每个被 include 文件os.ReadFile之后、yaml.Unmarshal之前
示例
servers:
- listen: "${LISTEN_ADDR}:8080"
ssl:
cert: "${SSL_CERT_PATH}"
key: "${SSL_KEY_PATH}"
security:
auth:
users:
- name: admin
password: "${ADMIN_PASSWORD_HASH}"
5. CI/CD 流水线
配置
.github/workflows/ci.yml 单文件,push to master + PR 触发。
Jobs
| Job | 依赖 | 步骤 |
|---|---|---|
lint |
无 | gofumpt 检查 → golangci-lint |
test |
无 | go test -race ./internal/... |
build |
lint + test | 多平台静态构建(linux/amd64, linux/arm64, darwin/amd64, darwin/arm64) |
docker |
build(仅 push/tag) | docker build + push |
文件
| 文件 | 操作 |
|---|---|
.github/workflows/ci.yml |
新建:GitHub Actions CI 流水线 |
约束
- Go 1.26
CGO_ENABLED=0- E2E 测试需要 Docker service(testcontainers)
- 使用
make fmt、make lint、make test命令
依赖关系
CORS ──────┐
Request-ID ┤── 全部独立,可并行
healthz ───┤
env interp ┤
CI/CD ─────┘
提交策略
每个特性一个独立 commit:
feat(middleware/cors): add CORS middleware with server-level configfeat(middleware/requestid): add request ID generation and propagationfeat(server): add /healthz and /readyz endpoints for k8s probesfeat(config): add ${ENV_VAR} interpolation in YAML configci: add GitHub Actions CI pipeline