docs(specs): add P0 production readiness design specification
This commit is contained in:
parent
f33117b940
commit
0c0cfd0485
@ -0,0 +1,230 @@
|
||||
# P0: 生产可用性特性设计
|
||||
|
||||
日期: 2026-06-11
|
||||
状态: Approved
|
||||
|
||||
## 概述
|
||||
|
||||
5 个独立特性,使 lolly 达到生产可部署状态。所有特性互不依赖,可完全并行开发。
|
||||
|
||||
---
|
||||
|
||||
## 1. CORS 中间件
|
||||
|
||||
### 配置
|
||||
|
||||
在 `security.cors` 下新增 `CORSConfig` 结构体,server 级别配置:
|
||||
|
||||
```yaml
|
||||
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.5,SecurityHeaders 之后、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` 下新增:
|
||||
|
||||
```yaml
|
||||
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}` 原样
|
||||
|
||||
调用位置:
|
||||
1. `Load()`: `os.ReadFile` 之后、`yaml.Unmarshal` 之前
|
||||
2. `processIncludes()`: 每个被 include 文件 `os.ReadFile` 之后、`yaml.Unmarshal` 之前
|
||||
|
||||
### 示例
|
||||
|
||||
```yaml
|
||||
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:
|
||||
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`
|
||||
Loading…
x
Reference in New Issue
Block a user