集成服务配置管理
概述
配置管理规范定义了集成服务的配置结构、校验方法和注入方式。遵循失快原则(Fail Fast),确保配置错误在应用启动时被发现。
核心原则
原则 1:配置结构化
使用结构体定义配置,而非零散的字符串变量。
go
// ✅ 正确:结构化配置
type StorageConfig struct {
Type string `toml:"type"`
Endpoint string `toml:"endpoint"`
AccessKey string `toml:"access_key"`
SecretKey string `toml:"secret_key"`
Bucket string `toml:"bucket"`
Timeout time.Duration `toml:"timeout"`
}
// ❌ 错误:零散变量
var (
storageType = "s3"
storageEndpoint = "https://s3.amazonaws.com"
storageAccessKey = "..."
)原则 2:配置校验
在 Client 初始化时校验配置,而非运行时失败。
go
// Validate 校验配置完整性
func (c *StorageConfig) Validate() error {
if c.Endpoint == "" {
return errors.New("storage endpoint is required")
}
if c.AccessKey == "" {
return errors.New("storage access_key is required")
}
if c.SecretKey == "" {
return errors.New("storage secret_key is required")
}
if c.Timeout == 0 {
c.Timeout = 30 * time.Second // 设置默认值
}
return nil
}原则 3:失快原则
配置错误应阻止应用启动,而非在运行时才发现。
go
func (a *App) Init(ctx context.Context, cfg *Config) error {
// ✅ 初始化失败时应用无法启动
storageClient, err := storage.NewClient(&cfg.Storage)
if err != nil {
return fmt.Errorf("failed to init storage client: %w", err)
}
a.storageClient = storageClient
return nil
}原则 4:敏感信息保护
敏感信息使用环境变量或密钥管理服务。
go
// ✅ 正确:从环境变量读取
type Config struct {
Storage StorageConfig `toml:"storage"`
}
func Load() (*Config, error) {
var cfg Config
// 从 TOML 文件加载非敏感配置
if err := toml.DecodeFile("config.toml", &cfg); err != nil {
return nil, err
}
// 从环境变量加载敏感信息
cfg.Storage.AccessKey = os.Getenv("STORAGE_ACCESS_KEY")
cfg.Storage.SecretKey = os.Getenv("STORAGE_SECRET_KEY")
return &cfg, nil
}配置定义
标准配置结构
go
// config/config.go
package config
import (
"errors"
"time"
)
// Config 应用配置
type Config struct {
// 基础设施
Server ServerConfig `toml:"server"`
Database DatabaseConfig `toml:"database"`
// 集成服务
Storage StorageConfig `toml:"storage"`
AI AIConfig `toml:"ai"`
Email EmailConfig `toml:"email"`
SMS SMSConfig `toml:"sms"`
Cache CacheConfig `toml:"cache"`
}
// StorageConfig 对象存储配置
type StorageConfig struct {
Type string `toml:"type"` // s3/cos/oss/local
Endpoint string `toml:"endpoint"`
Region string `toml:"region"`
AccessKey string `toml:"access_key"` // 从环境变量加载
SecretKey string `toml:"secret_key"` // 从环境变量加载
Bucket string `toml:"bucket"`
Timeout time.Duration `toml:"timeout"`
}
// Validate 校验配置
func (c *StorageConfig) Validate() error {
if c.Type == "" {
c.Type = "local" // 默认本地存储
}
if c.Type != "local" {
if c.Endpoint == "" {
return errors.New("storage endpoint is required")
}
if c.AccessKey == "" {
return errors.New("storage access_key is required")
}
if c.SecretKey == "" {
return errors.New("storage secret_key is required")
}
}
if c.Timeout == 0 {
c.Timeout = 30 * time.Second
}
return nil
}
// AIConfig AI 服务配置
type AIConfig struct {
Provider string `toml:"provider"` // openai/deepseek/claude
APIKey string `toml:"api_key"` // 从环境变量加载
BaseURL string `toml:"base_url"`
Model string `toml:"model"`
Timeout time.Duration `toml:"timeout"`
}
func (c *AIConfig) Validate() error {
if c.Provider == "" {
return errors.New("ai provider is required")
}
if c.APIKey == "" {
return errors.New("ai api_key is required")
}
if c.Model == "" {
return errors.New("ai model is required")
}
if c.Timeout == 0 {
c.Timeout = 60 * time.Second
}
return nil
}
// EmailConfig 邮件服务配置
type EmailConfig struct {
SMTPHost string `toml:"smtp_host"`
SMTPPort int `toml:"smtp_port"`
Username string `toml:"username"`
Password string `toml:"password"` // 从环境变量加载
From string `toml:"from"`
}
func (c *EmailConfig) Validate() error {
if c.SMTPHost == "" {
return errors.New("email smtp_host is required")
}
if c.SMTPPort == 0 {
c.SMTPPort = 587 // 默认端口
}
if c.Username == "" {
return errors.New("email username is required")
}
if c.Password == "" {
return errors.New("email password is required")
}
return nil
}配置加载
配置文件示例
toml
# config.toml
[server]
port = 8080
env = "development"
[database]
host = "localhost"
port = 3306
database = "myapp"
username = "root"
[storage]
type = "s3"
endpoint = "https://s3.amazonaws.com"
region = "us-west-2"
bucket = "my-app-uploads"
timeout = "30s"
[ai]
provider = "openai"
base_url = "https://api.openai.com/v1"
model = "gpt-4"
timeout = "60s"
[email]
smtp_host = "smtp.gmail.com"
smtp_port = 587
username = "noreply@example.com"
from = "MyApp <noreply@example.com>"加载函数
go
// config/loader.go
package config
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
)
// Load 加载配置
func Load(configPath string) (*Config, error) {
var cfg Config
// 1. 从 TOML 文件加载
if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
return nil, fmt.Errorf("failed to load config: %w", err)
}
// 2. 从环境变量加载敏感信息
cfg.Database.Password = os.Getenv("DB_PASSWORD")
cfg.Storage.AccessKey = os.Getenv("STORAGE_ACCESS_KEY")
cfg.Storage.SecretKey = os.Getenv("STORAGE_SECRET_KEY")
cfg.AI.APIKey = os.Getenv("AI_API_KEY")
cfg.Email.Password = os.Getenv("EMAIL_PASSWORD")
// 3. 校验配置
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("invalid config: %w", err)
}
return &cfg, nil
}
// Validate 校验所有配置
func (c *Config) Validate() error {
if err := c.Storage.Validate(); err != nil {
return fmt.Errorf("storage config: %w", err)
}
if err := c.AI.Validate(); err != nil {
return fmt.Errorf("ai config: %w", err)
}
if err := c.Email.Validate(); err != nil {
return fmt.Errorf("email config: %w", err)
}
return nil
}Client 初始化
配置注入到 Client
go
// clients/storage/client.go
package storage
type Client struct {
config *config.StorageConfig
// ...
}
func NewClient(cfg *config.StorageConfig) (*Client, error) {
// 1. 校验配置
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("invalid storage config: %w", err)
}
// 2. 根据配置初始化
client := &Client{
config: cfg,
}
// 3. 初始化第三方 SDK
switch cfg.Type {
case "s3":
// 初始化 S3 客户端
case "cos":
// 初始化 COS 客户端
case "local":
// 初始化本地存储
}
return client, nil
}App 容器初始化
go
// app/app.go
func (a *App) Init(ctx context.Context, cfg *config.Config) error {
a.config = cfg
// 初始化 Clients
if err := a.initClients(); err != nil {
return err
}
return nil
}
func (a *App) initClients() error {
// 对象存储
storageClient, err := storage.NewClient(&a.config.Storage)
if err != nil {
return fmt.Errorf("failed to init storage client: %w", err)
}
a.storageClient = storageClient
// AI 服务
aiClient, err := ai.NewClient(&a.config.AI)
if err != nil {
return fmt.Errorf("failed to init ai client: %w", err)
}
a.aiClient = aiClient
// 邮件服务
emailClient, err := email.NewClient(&a.config.Email)
if err != nil {
return fmt.Errorf("failed to init email client: %w", err)
}
a.emailClient = emailClient
return nil
}环境变量
敏感信息环境变量
必须通过环境变量加载的敏感信息:
- 数据库密码:
DB_PASSWORD - 对象存储密钥:
STORAGE_ACCESS_KEY,STORAGE_SECRET_KEY - AI API 密钥:
AI_API_KEY - 邮件密码:
EMAIL_PASSWORD - 短信服务密钥:
SMS_API_KEY
环境变量示例
bash
# .env
DB_PASSWORD=your_db_password
STORAGE_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
STORAGE_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AI_API_KEY=sk-...
EMAIL_PASSWORD=your_email_password最佳实践
1. 默认值设置
在 Validate 方法中设置默认值:
go
func (c *StorageConfig) Validate() error {
if c.Timeout == 0 {
c.Timeout = 30 * time.Second // 默认超时
}
if c.Type == "" {
c.Type = "local" // 默认类型
}
return nil
}2. 配置示例文件
提供 config.toml.example 文件:
toml
# config.toml.example
# 复制此文件为 config.toml 并填写实际配置
[storage]
type = "s3"
endpoint = "https://s3.amazonaws.com"
region = "us-west-2"
bucket = "your-bucket-name"
# access_key 和 secret_key 从环境变量加载3. 配置文档
在 README 中说明配置项:
markdown
## 配置
### 环境变量
- `DB_PASSWORD`: 数据库密码(必填)
- `STORAGE_ACCESS_KEY`: 对象存储访问密钥(必填)
- `STORAGE_SECRET_KEY`: 对象存储密钥(必填)
- `AI_API_KEY`: AI API 密钥(必填)
### 配置文件
复制 `config.toml.example` 为 `config.toml` 并修改配置。检查清单
- [ ] 配置使用结构体定义,不使用零散变量
- [ ] 每个配置结构体实现
Validate()方法 - [ ] 敏感信息从环境变量加载,不写入配置文件
- [ ] Client 初始化时校验配置
- [ ] 配置错误阻止应用启动(失快原则)
- [ ] 提供配置示例文件(
config.toml.example) - [ ] 在 Validate 中设置默认值
相关规范
- Client 层设计 - Client 如何使用配置
- 依赖注入规范 - 配置注入到 Client