最佳实践
本指南提供了使用 Apq.Cfg 进行配置管理的最佳实践,帮助开发者构建更加健壮、安全和可维护的配置系统。
1. 配置层级设计
1.1 层级原则
配置层级是 Apq.Cfg 的核心概念,数值越大优先级越高。建议遵循以下层级设计原则:
- 0-2层:系统默认值和基础配置,通常包含在应用程序包中
- 3-5层:环境特定配置,如开发、测试、预发布环境
- 6-8层:租户/用户特定配置,允许覆盖环境配置
- 9-10层:运行时动态配置,具有最高优先级
1.2 层级示例
csharp
var cfg = new CfgBuilder()
// 0层:系统默认值
.AddJson("config.json", level: 0, writeable: false)
// 1层:环境特定默认值
.AddJson($"config.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",
level: 1, writeable: false)
// 5层:环境变量
.AddEnvironmentVariables(level: 5, prefix: "APP_")
// 8层:用户特定配置(可选)
.AddJson("user.config.json", level: 8, writeable: true, isPrimaryWriter: true)
.Build();2. 安全配置管理
2.1 敏感数据处理
避免将敏感信息(如密码、API密钥)直接存储在配置文件中:
csharp
// 推荐:使用外部配置中心
var cfg = new CfgBuilder()
.AddJson("config.json", level: 0, writeable: false)
.AddSource(new VaultCfgSource("secret/", level: 5, token: vaultToken)) // 从 HashiCorp Vault 加载
.Build();
// 避免:直接在配置文件中存储敏感信息
// {
// "ConnectionStrings": {
// "DefaultConnection": "Server=myServerAddress;Database=myDataBase;User Id=myUsername;password=myPassword;"
// }
// }2.2 配置加密
对需要本地存储的敏感配置进行加密:
csharp
// 使用自定义加密配置源
var cfg = new CfgBuilder()
.AddJson("config.json", level: 0, writeable: false)
.AddSource(new EncryptedJsonCfgSource("config.encrypted.json", level: 2,
writeable: false, encryptionKey: encryptionKey))
.Build();2.3 访问控制
实施最小权限原则,限制配置源的访问权限:
csharp
// 生产环境配置文件权限设置
// - config.json: 只读 (应用程序服务账户)
// - config.Production.json: 只读 (配置管理服务账户)
// - config.Development.json: 读写 (开发者账户)3. 环境隔离
3.1 环境特定配置
为不同环境维护独立的配置文件:
/config
├── config.json # 基础配置
├── config.Development.json # 开发环境覆盖
├── config.Staging.json # 预发布环境覆盖
└── config.Production.json # 生产环境覆盖3.2 环境变量约定
建立清晰的环境变量命名约定:
csharp
// 推荐:使用层次化命名
var cfg = new CfgBuilder()
.AddEnvironmentVariables(level: 5, prefix: "APP_")
// APP_ConnectionStrings__DefaultConnection
// APP_Logging__LogLevel__Default
// APP_Caching__Redis__ConnectionString
.Build();
// 避免:扁平化命名
// APP_CONNECTIONSTRING
// APP_LOGLEVEL
// APP_REDIS_CONNECTION4. 配置验证
4.1 启动时验证
在应用程序启动时验证关键配置:
csharp
public class AppSettings
{
public string ConnectionString { get; set; }
public int RetryCount { get; set; }
public TimeSpan Timeout { get; set; }
[ValidateComplexType]
public class ValidateComplexType : ValidationAttribute
{
public override bool IsValid(object? value)
{
if (value is AppSettings settings)
{
// 验证连接字符串格式
if (string.IsNullOrWhiteSpace(settings.ConnectionString))
return false;
// 验证重试次数范围
if (settings.RetryCount < 0 || settings.RetryCount > 10)
return false;
// 验证超时时间范围
if (settings.Timeout < TimeSpan.FromSeconds(1) || settings.Timeout > TimeSpan.FromMinutes(5))
return false;
return true;
}
return false;
}
}
}
// 使用
var cfg = new CfgBuilder()
.AddJson("config.json", level: 0, writeable: false)
.Build();
var settings = ObjectBinder.Bind<AppSettings>(cfg.GetSection("App"));
Validator.ValidateObject(settings, new ValidationContext(settings), validateAllProperties: true);4.2 配置健康检查
实施配置健康检查机制:
csharp
// 定期检查关键配置的有效性
public class ConfigurationHealthCheck : IHealthCheck
{
private readonly ICfgRoot _cfg;
public ConfigurationHealthCheck(ICfgRoot cfg)
{
_cfg = cfg;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
// 验证数据库连接
var connectionString = _cfg.Get<string>("ConnectionStrings:DefaultConnection");
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy("数据库连接失败", ex);
}
}
}5. 配置变更管理
5.1 配置版本控制
对配置文件实施版本控制:
json
{
"Version": "1.2.0",
"Info": {
"Description": "应用程序主配置文件",
"LastModified": "2023-12-01T10:30:00Z",
"ModifiedBy": "admin@example.com"
},
"ConnectionStrings": {
"DefaultConnection": "..."
}
}5.2 配置变更审计
记录配置变更历史:
csharp
public class ConfigurationAuditService
{
private readonly ICfgRoot _cfg;
private readonly IAuditLogger _auditLogger;
public ConfigurationAuditService(ICfgRoot cfg, IAuditLogger auditLogger)
{
_cfg = cfg;
_auditLogger = auditLogger;
// 订阅配置变更事件
_cfg.ConfigChanges.Subscribe(OnConfigurationChanged);
}
private void OnConfigurationChanged(ConfigChangeEvent change)
{
_auditLogger.LogInformation("配置已更改: {Key}={Key}, OldValue={OldValue}, NewValue={NewValue}, ChangedAt={ChangedAt}",
change.Key,
change.OldValue,
change.NewValue,
DateTime.UtcNow);
}
}6. 性能优化
6.1 配置缓存
缓存频繁访问的配置值:
csharp
public class CachedConfigurationService
{
private readonly ICfgRoot _cfg;
private readonly IMemoryCache _cache;
private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(5);
public CachedConfigurationService(ICfgRoot cfg, IMemoryCache cache)
{
_cfg = cfg;
_cache = cache;
}
public T Get<T>(string key)
{
var cacheKey = $"cfg_{key}";
return _cache.GetOrCreate(cacheKey, () => _cfg.Get<T>(key), _cacheDuration);
}
}6.2 批量操作
使用批量操作减少配置源访问:
csharp
// 推荐:使用批量获取
var keys = new[] { "FeatureA:Enabled", "FeatureB:Enabled", "FeatureC:Enabled" };
var features = cfg.GetMany(keys);
// 避免:多次单独获取
var featureA = cfg.Get<bool>("FeatureA:Enabled");
var featureB = cfg.Get<bool>("FeatureB:Enabled");
var featureC = cfg.Get<bool>("FeatureC:Enabled");7. 监控和诊断
7.1 配置使用监控
监控配置访问模式:
csharp
public class ConfigurationMetrics
{
private readonly IMetrics _metrics;
private readonly ConcurrentDictionary<string, int> _accessCounts = new();
public void RecordAccess(string key)
{
_accessCounts.AddOrUpdate(key, 1, (_, count) => count + 1);
_metrics.Counter("configuration.access.count").Inc();
_metrics.Histogram("configuration.access.frequency").Observe(_accessCounts.Count);
}
public void ReportTopAccessedKeys()
{
var topKeys = _accessCounts.OrderByDescending(kvp => kvp.Value)
.Take(10)
.Select(kvp => kvp.Key);
_metrics.Gauge("configuration.top_keys", topKeys);
}
}7.2 配置加载诊断
诊断配置加载问题:
csharp
public class ConfigurationDiagnostics
{
public static void LogConfigurationSources(ICfgRoot cfg)
{
var sb = new StringBuilder();
sb.AppendLine("配置源加载情况:");
// 这里需要访问内部实现,实际项目中可能需要添加诊断API
foreach (var source in cfg.GetSources())
{
sb.AppendLine($"- {source.GetType().Name}: Level={source.Level}, Writeable={source.IsWriteable}");
}
// 记录到日志或诊断系统
Console.WriteLine(sb.ToString());
}
}8. 故障排除
8.1 常见问题
配置未生效
- 检查配置层级是否正确
- 确认配置键名拼写正确
- 验证配置源是否成功加载
配置值类型转换错误
- 检查目标类型是否匹配
- 确认字符串格式正确
- 使用 TryGet 方法避免异常
配置热重载不工作
- 确认配置源支持热重载
- 检查文件权限
- 验证文件监控机制
8.2 调试技巧
启用详细日志
csharpvar cfg = new CfgBuilder() .AddJson("config.json", level: 0, writeable: false) .AddEnvironmentVariables(level: 5, prefix: "APP_") .WithEncodingDetectionLogging(result => Console.WriteLine($"编码检测: {result}")) .Build();导出配置快照
csharp// 导出当前配置快照用于调试 var snapshot = cfg.ExportSnapshot(); File.WriteAllText("debug-config.json", snapshot);配置诊断工具
csharp// 创建诊断工具 var diagnostics = new ConfigurationDiagnostics(cfg); diagnostics.ValidateRequiredKeys(); diagnostics.ReportConflictingValues(); diagnostics.CheckForDeprecatedSettings();