lolly/docs/superpowers/specs/2026-06-11-p0-production-readiness-design.md

6.5 KiB
Raw Blame History

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: 是否允许发送 Cookie
  • max_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.5SecurityHeaders 之后、ErrorIntercept 之前)

行为

  1. 非 CORS 请求(无 Origin 头):直接 pass-through
  2. Preflight (OPTIONS):返回 204 + CORS 头,不进入后续 handler
  3. 实际请求:调用 next(ctx) 后添加 CORS 响应头
  4. 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 之前)

行为

  1. 检查入站 X-Request-ID 请求头
  2. 有值 → 复用(信任下游),存入 ctx.SetUserValue("request_id", id)
  3. 无值 → 生成 UUID v4存入 ctx.SetUserValue
  4. 始终在响应中设置 X-Request-ID
  5. 代理转发时,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 修改:添加 HealthzConfigReadyzConfig
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} 原样

调用位置:

  1. Load(): os.ReadFile 之后、yaml.Unmarshal 之前
  2. 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 servicetestcontainers
  • 使用 make fmtmake lintmake test 命令

依赖关系

CORS ──────┐
Request-ID ┤── 全部独立,可并行
healthz ───┤
env interp ┤
CI/CD ─────┘

提交策略

每个特性一个独立 commit

  1. feat(middleware/cors): add CORS middleware with server-level config
  2. feat(middleware/requestid): add request ID generation and propagation
  3. feat(server): add /healthz and /readyz endpoints for k8s probes
  4. feat(config): add ${ENV_VAR} interpolation in YAML config
  5. ci: add GitHub Actions CI pipeline