Files
cs-note/hzh/Gen2D/15-配置级联机制.md
T

198 lines
6.2 KiB
Markdown
Raw Normal View History

2026-06-03 10:12:49 +08:00
# 15. 配置级联机制
> **一句话概括**:Viper 三层配置级联 — YAML 文件 -> 环境变量 -> 默认值,一处配置随处运行。
---
## 配置加载流程
```mermaid
flowchart LR
subgraph "配置源(优先级从低到高)"
D["默认值\nsetDefaults()"]
Y["YAML 文件\nconfig.yaml"]
E["环境变量\n.env / system env"]
end
subgraph "Viper 引擎"
V["Viper\n合并 + 覆盖"]
end
subgraph "输出"
C["Config Struct\n全局配置对象"]
S["各组件\nServer / DB / Redis / ..."]
end
D -->|"1. 设置默认值"| V
Y -->|"2. 读取 YAML"| V
E -->|"3. 绑定 ENV"| V
V -->|"Unmarshal"| C
C -->|"注入"| S
style D fill:#9e9e9e,stroke:#333,color:#fff
style Y fill:#2196f3,stroke:#333,color:#fff
style E fill:#f44336,stroke:#333,color:#fff
style V fill:#ff9800,stroke:#333,color:#fff
```
**优先级规则**:`ENV > YAML > Default`
环境变量始终拥有最高优先级,可以覆盖任何 YAML 配置;YAML 配置覆盖默认值。
---
## 配置结构体
```go
type Config struct {
Server ServerConfig // HTTP 服务
Database DatabaseConfig // MySQL 数据库
JWT JWTConfig // JWT 签名
Log LogConfig // 日志
Redis RedisConfig // Redis 缓存
LLM LLMConfig // 大语言模型
ImageGen ImageGenConfig // 文生图 API
Qiniu QiniuConfig // 七牛云存储
WorkerPool WorkerPoolConfig // 协程池
TaskQueue TaskQueueConfig // 任务队列
}
```
| 配置块 | 关键字段 | 默认值 |
|--------|----------|--------|
| `Server` | `port`, `mode` | `8080`, `debug` |
| `Database` | `dsn` | `gen2d:password@tcp(127.0.0.1:3306)/gen2d` |
| `JWT` | `secret`, `expire` | `gen2d-dev-secret`, `7200s` |
| `Log` | `level`, `format` | `info`, `text` |
| `Redis` | `addr`, `password`, `db` | `localhost:6379`, `""`, `0` |
| `LLM` | `base_url`, `api_key`, `model` | `api.openai.com/v1`, `""`, `gpt-4o` |
| `ImageGen` | `base_url`, `model`, `timeout` | `api.suchuang.vip/v1`, `gpt-image-2-token`, `120s` |
| `Qiniu` | `bucket`, `cdn_host`, `url_expire` | `""`, `""`, `3600s` |
| `WorkerPool` | `workers`, `queue_size`, `max_per_user` | `NumCPU*4`, `100`, `2` |
| `TaskQueue` | `driver`, `memory.buffer_size` | `memory`, `100` |
---
## 环境变量绑定
每个配置字段都有对应的环境变量绑定,命名规则为 `GEN2D_` 前缀 + 大写下划线格式:
```go
func bindEnvVars(v *viper.Viper) {
v.BindEnv("server.port", "GEN2D_PORT")
v.BindEnv("server.mode", "GEN2D_MODE")
v.BindEnv("database.dsn", "GEN2D_DSN")
v.BindEnv("jwt.secret", "GEN2D_JWT_SECRET")
v.BindEnv("llm.api_key", "GEN2D_LLM_API_KEY")
v.BindEnv("redis.addr", "GEN2D_REDIS_ADDR")
v.BindEnv("qiniu.access_key", "GEN2D_QINIU_ACCESS_KEY")
// ... 共 25+ 个绑定
}
```
| 配置路径 | 环境变量 | 说明 |
|----------|----------|------|
| `server.port` | `GEN2D_PORT` | HTTP 端口 |
| `server.mode` | `GEN2D_MODE` | `debug` / `release` |
| `database.dsn` | `GEN2D_DSN` | MySQL 连接串 |
| `jwt.secret` | `GEN2D_JWT_SECRET` | JWT 签名密钥 |
| `llm.base_url` | `GEN2D_LLM_BASE_URL` | LLM API 地址 |
| `llm.api_key` | `GEN2D_LLM_API_KEY` | LLM API 密钥 |
| `image_gen.base_url` | `GEN2D_IMAGE_BASE_URL` | 文生图 API 地址 |
| `redis.addr` | `GEN2D_REDIS_ADDR` | Redis 地址 |
| `taskqueue.driver` | `GEN2D_TASKQUEUE_DRIVER` | `memory` / `rabbitmq` |
---
## 使用场景
```mermaid
flowchart TD
subgraph "开发环境"
D1["config.yaml\n本地配置文件"]
D2[".env\n环境变量覆盖敏感值"]
D3["默认值\n开箱即用"]
end
subgraph "测试环境"
T1["环境变量\nCI/CD 管线注入"]
T2["默认值\n兜底"]
end
subgraph "生产环境"
P1["K8s ConfigMap\n非敏感配置"]
P2["K8s Secret\n敏感配置(密钥、DSN)"]
P3["默认值\n兜底"]
end
D1 --> D3
D2 --> D1
T1 --> T2
P1 --> P3
P2 --> P1
```
| 环境 | 主配置源 | 敏感配置 | 说明 |
|------|----------|----------|------|
| 开发 | `config.yaml` | `.env` 文件 | 直观、可版本控制 |
| 测试 | 环境变量 | 环境变量 | CI/CD 管线注入 |
| 生产 | ConfigMap | Secret | K8s 原生管理 |
---
## 加载过程详解
```mermaid
sequenceDiagram
participant Main as main.go
participant Viper
participant YAML as config.yaml
participant ENV as 环境变量
participant Cfg as Config Struct
Main->>Viper: Load()
Main->>Viper: setDefaults(v)
Note over Viper: 设置 25+ 默认值
Viper->>YAML: ReadInConfig()
YAML-->>Viper: 配置内容
Viper->>Viper: bindEnvVars(v)
Note over Viper: 绑定 25+ 环境变量
Viper->>ENV: 读取环境变量
ENV-->>Viper: 覆盖对应字段
Viper->>Cfg: Unmarshal(&cfg)
Cfg-->>Main: 返回完整配置
```
关键步骤:
1. **加载 `.env` 文件** — `godotenv.Load()` 从项目根目录读取 `.env`
2. **设置默认值** — `setDefaults(v)` 为所有字段提供合理的默认值
3. **读取 YAML** — `v.ReadInConfig()` 从 `internal/config/config.yaml` 加载
4. **绑定环境变量** — `bindEnvVars(v)` 将每个字段映射到 `GEN2D_*` 环境变量
5. **反序列化** — `v.Unmarshal(&cfg)` 将合并后的配置映射到 Go 结构体
> **容错设计**:YAML 文件不存在时不报错,仅使用环境变量 + 默认值。这确保了零配置即可启动。
---
## 与部署的关系
配置级联机制与部署架构紧密配合:
| 部署方式 | 配置策略 | 示例 |
|----------|----------|------|
| `go run` 本地开发 | YAML + `.env` | `config.yaml` 配置 DB,`.env` 配置 API Key |
| `docker compose` | `.env` 文件注入 | `env_file: ./backend/.env` |
| Kubernetes | ConfigMap + Secret | `GEN2D_DSN` 从 Secret 注入 |
---
## 关联文档
| 文档 | 关系 |
|------|------|
| [14 - 部署架构](14-deployment.md) | 配置管理在部署中的应用 |
| [01 - 系统总览](01-system-overview.md) | 配置在启动流程中的位置 |
| [02 - 协程池](02-worker-pool.md) | WorkerPoolConfig 控制并发参数 |
| [00 - 索引](00-index.md) | 返回文档总览 |