ConfigStore 架构详解
本文档面向库开发者和维护者,深入解析 ConfigStore 的实现架构、设计理念和扩展机制。
1. 设计理念
1.1 四层存储架构
ConfigStore 采用四层优先级架构,实现了配置的层次化管理和灵活覆盖:
bash
+-----------------------------------------------+
| Temp Layer (最高优先级) |
| - 仅内存存储,进程退出后丢失 |
| - 用于临时覆盖、测试场景 |
+-----------------------------------------------+
| App Layer |
| - 应用级配置,相对路径 config/app.ini |
| - 运行时可写,应用专用配置 |
+-----------------------------------------------+
| User Layer |
| - 用户级配置,~/.config/cfdesktop/user.ini |
| - 用户个性化设置,持久化存储 |
+-----------------------------------------------+
| System Layer (最低优先级) |
| - 系统级配置,{app_dir}/system.ini (CFDesktop 自管理目录) |
| - 全局默认配置,只读或需要特权写入 |
+-----------------------------------------------+
```cpp
**查询顺序(优先级从高到低)**:Temp -> App -> User -> System
这种设计允许:
- 系统管理员提供全局默认值(System)
- 用户自定义个人偏好(User)
- 应用程序维护运行时配置(App)
- 开发者进行临时测试和调试(Temp)
### 1.2 Pimpl 模式
ConfigStore 使用 Pimpl(Pointer to Implementation)模式实现接口与实现分离:
```cpp
+------------------+ +------------------------+
| ConfigStore | | ConfigStoreImpl |
| (公共接口层) | --------> | (实现层) |
| | | |
| - 模板方法 | | - QSettings 管理 |
| - KeyHelper 调用 | | - 缓存管理 |
| - 单例继承 | | - Watcher 机制 |
| | | - 线程同步 |
+------------------+ +------------------------+
```text
**优势**:
1. **ABI 稳定性**:实现变更不影响公共头文件,无需重新编译依赖代码
2. **编译隔离**:QSettings 等依赖头文件仅在实现文件中包含
3. **接口简洁**:公共 API 头文件仅暴露必要接口
4. **灵活性**:可替换实现而保持接口不变
### 1.3 单例模式
ConfigStore 继承自 `SimpleSingleton<ConfigStore>`,使用 Meyer's 单例模式:
```cpp
// base/include/base/singleton/simple_singleton.hpp
template <typename SingleTargetClass>
class SimpleSingleton {
static SingleTargetClass& instance() {
static SingleTargetClass target; // C++11 保证线程安全
return target;
}
};
```text
**特点**:
- **线程安全**:C++11 标准保证静态局部变量初始化的线程安全性
- **延迟初始化**:首次调用 `instance()` 时才创建实例
- **自动销毁**:程序退出时自动析构
- **不可复制**:删除了拷贝和移动构造函数
## 2. 组件详解
### 2.1 ConfigStore:公共接口层
文件位置:`desktop/base/config_manager/include/cfconfig.hpp`
**核心职责**:
1. 提供类型安全的模板 API
2. 处理 KeyView 到 Key 的转换
3. 委托实际操作给 ConfigStoreImpl
**关键模板方法**:
```cpp
// 查询配置(带默认值)
template <typename Value>
[[nodiscard]] Value query(const KeyView key, const Value& default_value);
// 设置配置
template <typename Value>
[[nodiscard]] bool set(const KeyView key, const Value& v,
Layer layer = Layer::App,
NotifyPolicy notify_policy = NotifyPolicy::Immediate);
// 注册键
template <typename Value>
[[nodiscard]] RegisterResult register_key(const Key& key, const Value& init_value,
Layer layer = Layer::App,
NotifyPolicy notify_policy = NotifyPolicy::Immediate);
```text
**类型转换机制**(`detail::any_cast`):
- 直接类型匹配:`std::any` 直接包含目标类型
- QVariant 转换:处理 Qt 类型系统
- 字符串与数值互转:增强兼容性
### 2.2 ConfigStoreImpl:实现层
文件位置:
- 头文件:`desktop/base/config_manager/src/impl/config_impl.h`
- 实现文件:`desktop/base/config_manager/src/impl/config_impl.cpp`
**核心成员变量**:
```cpp
class ConfigStoreImpl {
private:
// 线程同步
mutable std::shared_mutex mutex_; // 读写锁
std::mutex deferred_mutex_; // 延迟回调专用锁
// 路径提供者
std::shared_ptr<IConfigStorePathProvider> path_provider_;
// 存储(Temp 层仅使用 cache_)
std::unordered_map<std::string, std::any> cache_;
std::unique_ptr<QSettings> settings_system_;
std::unique_ptr<QSettings> settings_user_;
std::unique_ptr<QSettings> settings_app_;
// 脏标记
std::array<bool, 4> dirty_flags_;
// Watcher 管理
std::vector<WatcherEntry> watchers_;
std::atomic<WatcherHandle> next_handle_;
// 延迟通知
std::vector<PendingChange> pending_changes_;
std::vector<DeferredWatcherEvent> deferred_events_;
};
```text
**内部方法架构**:
每个公共方法都有对应的 `_impl` 版本,用于已持锁场景:
```cpp
公共方法 (获取锁) 内部方法 (无锁,调用者必须持锁)
+------------------+ +------------------------+
| set() | | set_impl() |
| register_key() | ------->| register_key_impl() |
| unregister_key() | | unregister_key_impl() |
| clear() | | clear_impl() |
| clear_layer() | | clear_layer_impl() |
+------------------+ +------------------------+
```text
这种设计避免了在已持锁场景下的重复加锁,提高了效率。
### 2.3 IConfigStorePathProvider:路径提供者
文件位置:`desktop/base/config_manager/include/cfconfig/cfconfig_path_provider.h`
**接口设计**:
```cpp
class IConfigStorePathProvider {
public:
virtual QString system_path() const = 0;
virtual QString user_dir() const = 0;
virtual QString user_filename() const = 0;
virtual QString app_dir() const = 0;
virtual QString app_filename() const = 0;
virtual bool is_layer_enabled(int layer_index) const = 0;
};
```bash
**默认实现**:`DesktopConfigStorePathProvider`
| 层级 | 路径格式 | 说明 |
|------|----------------------------|--------------------------|
| System | `{app_dir}/system.ini` | 系统配置 (CFDesktop 自管理目录) |
| User | `~/.config/cfdesktop/user.ini` | 用户配置,自动创建目录 |
| App | `config/app.ini` | 相对路径,运行时可写 |
| Temp | (内存) | 不持久化 |
### 2.4 KeyHelper:键辅助类
文件位置:`desktop/base/config_manager/include/cfconfig_key.h`
**数据结构**:
```cpp
struct KeyView {
std::string_view group; // 分组,如 "app.theme"
std::string_view key; // 键名,如 "name"
};
struct Key {
std::string full_key; // 完整键,如 "app.theme.name"
std::string full_description; // 完整描述
};
```text
**转换逻辑**:
```cpp
// KeyView -> Key
"app.theme" + "name" => "app.theme.name"
// Key -> KeyView
"app.theme.name" => group="app.theme", key="name"
```text
**验证规则**(`default_policy`):
- 只允许字母、数字、下划线和点号
- 拒绝特殊字符防止路径遍历和注入
## 3. 数据流
### 3.1 读取流程
```cpp
用户调用 ConfigStore::query()
|
v
KeyHelper 转换 KeyView -> Key
|
v
获取 shared_lock (读锁)
|
v
ConfigStoreImpl::query()
|
+-> 检查 cache_ (Temp 层)
| |
| +-> 命中? 返回值
|
+-> 查询 App 层 (QSettings)
| |
| +-> 命中? 缓存到 cache_ 并返回
|
+-> 查询 User 层 (QSettings)
| |
| +-> 命中? 缓存到 cache_ 并返回
|
+-> 查询 System 层 (QSettings)
| |
| +-> 命中? 缓存到 cache_ 并返回
|
+-> 都未命中? 返回默认值
|
v
释放 shared_lock
|
v
类型转换 (any_cast)
|
v
返回给用户
```text
### 3.2 写入流程
```cpp
用户调用 ConfigStore::set()
|
v
KeyHelper 转换 KeyView -> Key
|
v
获取 unique_lock (写锁)
|
v
ConfigStoreImpl::set()
|
+-> 保存旧值 (用于通知)
|
+-> set_impl()
| |
| +-> [Temp 层]
| | 更新 cache_
| |
| +-> [其他层]
| 更新 QSettings
| 更新 cache_
| mark_dirty(layer)
|
+-> 根据 NotifyPolicy
| |
| +-> Immediate: trigger_watchers()
| +-> Manual: 加入 pending_changes_
|
v
释放 unique_lock
|
v
execute_deferred_watchers()
|
+-> 获取 deferred_mutex_
+-> 移动 deferred_events_ 到局部变量
+-> 释放 deferred_mutex_
+-> 执行所有回调 (无主锁)
|
v
返回结果
```text
### 3.3 Watcher 触发机制
```cpp
配置变更发生
|
v
trigger_watchers() [持锁状态]
|
+-> 遍历所有 watchers_
|
+-> 对每个 watcher
| |
| +-> match_pattern(pattern, key)
| |
| +-> 匹配? 创建 DeferredWatcherEvent
| 加入 deferred_events_
|
v
释放主锁后
|
v
execute_deferred_watchers() [无锁状态]
|
+-> 移动 deferred_events_ 到局部
|
+-> 执行每个回调
callback(key, old_value, new_value, layer)
|
v
完成
```text
**延迟回调机制的关键**:
1. 在主锁内收集事件(避免回调中死锁)
2. 释放主锁后再执行回调(允许回调中访问 ConfigStore)
3. 使用独立的 `deferred_mutex_` 保护事件队列
### 3.4 四层优先级查询图
```cpp
查询 "app.theme.name"
|
+----------------+----------------+
| | |
v v v
[Temp] [App] [User] [System]
cache_ QSettings QSettings QSettings
| | | |
| | | |
+---> 检查 ----+---> 检查 -----+---> 检查 ---+---> 检查
| | | |
v v v v
命中? 命中? 命中? 命中?
| | | |
是 否 否 否
| | | |
v v v v
返回值 -------> 下层 ---------> 下层 --------> 默认值
```text
## 4. 线程安全
### 4.1 读写锁实现原理
ConfigStoreImpl 使用 `std::shared_mutex` 实现读写分离:
```cpp
mutable std::shared_mutex mutex_;
// 读操作:共享锁,允许多个读操作并发
std::shared_lock lock(mutex_); // query(), has_key()
// 写操作:独占锁,独占访问
std::unique_lock lock(mutex_); // set(), register_key(), etc.
```bash
**并发场景分析**:
| 场景 | 是否允许 | 说明 |
|------|---------|------|
| 多读并发 | 是 | shared_lock 可共享 |
| 读写并发 | 否 | 读锁和写锁互斥 |
| 多写并发 | 否 | unique_lock 独占 |
**锁粒度**:
- 细粒度:每个操作方法独立加锁
- 范围:覆盖所有内部状态访问
- 短暂:仅保护临界区,回调在锁外执行
### 4.2 延迟回调机制
**问题**:如果在持锁状态下调用回调,可能导致:
1. 死锁:回调中再次访问 ConfigStore 尝试获取锁
2. 饥饿:回调耗时过长阻塞其他操作
3. 递归锁:违反设计约束
**解决方案**:两阶段回调
```cpp
// 阶段 1:收集事件(持锁)
void trigger_watchers(...) {
for (const auto& watcher : watchers_) {
if (match_pattern(...)) {
deferred_events_.push_back(...); // 仅收集,不执行
}
}
}
// 阶段 2:执行回调(无锁)
void execute_deferred_watchers() {
std::vector<DeferredWatcherEvent> events;
{
std::lock_guard<std::mutex> lock(deferred_mutex_);
events = std::move(deferred_events_);
deferred_events_.clear();
}
for (const auto& event : events) {
event.callback(...); // 安全执行,无主锁
}
}
```text
**锁分离设计**:
- `mutex_`:保护配置数据和 watcher 列表
- `deferred_mutex_`:保护延迟事件队列
### 4.3 并发控制边界
**原子操作**:
```cpp
std::atomic<WatcherHandle> next_handle_{1};
// WatcherHandle 分配无需加锁
```text
**内存序保证**:
- 默认使用 `memory_order_seq_cst`
- 确保多线程环境下 Handle 唯一性
**无锁场景**:
- WatcherHandle 分配(原子操作)
- 延迟回调执行(独立锁)
**有锁场景**:
- 配置读写(shared_mutex)
- Watcher 注册/注销(unique_lock)
- 事件队列操作(deferred_mutex_)
## 5. 扩展点
### 5.1 自定义 KeyHelper
**注意**:当前实现中,ConfigStore 的模板方法内部直接创建了 `KeyHelper` 局部实例,因此通过继承扩展 KeyHelper 不会生效。如需自定义键转换逻辑,需要修改 ConfigStore 模板方法的实现。
**未来扩展方向**:可以通过依赖注入方式支持自定义 KeyHelper:
```cpp
// 当前实现(不支持扩展)
template <typename Value>
Value ConfigStore::query(const KeyView key, const Value& default_value) {
KeyHelper helper; // 直接创建,无法替换
Key k;
if (!helper.fromKeyViewToKey(key, k)) {
return default_value;
}
// ...
}
// 建议的扩展方式(需要修改源码)
class ConfigStore {
std::unique_ptr<KeyHelper> key_helper_; // 使用指针支持多态
public:
void set_key_helper(std::unique_ptr<KeyHelper> helper);
};
```text
### 5.2 自定义路径提供者
**场景**:测试、嵌入式系统、跨平台适配
**实现方式**:继承 `IConfigStorePathProvider`
```cpp
#include <cfconfig/cfconfig_path_provider.h>
class TestPathProvider : public cf::config::IConfigStorePathProvider {
public:
~TestPathProvider() override = default;
explicit TestPathProvider(const QString& base_dir)
: base_dir_(base_dir) {}
QString system_path() const override {
return base_dir_ + "/system.ini";
}
QString user_dir() const override {
return base_dir_ + "/user";
}
QString user_filename() const override {
return "user.ini";
}
QString app_dir() const override {
return base_dir_ + "/app";
}
QString app_filename() const override {
return "app.ini";
}
bool is_layer_enabled(int layer_index) const override {
// 禁用 System 层
return layer_index != 0; // System = 0
}
private:
QString base_dir_;
};
```text
**使用方式**:
```cpp
auto test_provider = std::make_shared<TestPathProvider>("/tmp/test_config");
cf::config::ConfigStore::instance().initialize(test_provider);
```text
### 5.3 扩展存储后端
**场景**:需要使用非 QSettings 的存储方式
**实现方式**:修改 ConfigStoreImpl 或创建新的实现类
**注意**:当前 ConfigStoreImpl 没有抽象基类,如需完全替换存储后端,需要:
1. **方案一**:直接修改 `config_impl.cpp` 中的存储逻辑
2. **方案二**:定义接口并重构 ConfigStoreImpl
**当前存储接口摘要**(需要实现的核心方法):
```cpp
class ConfigStoreImpl {
// 查询操作
std::any query(const std::string& key, const std::any& default_value);
std::any query(const std::string& key, Layer layer);
// 写入操作
bool set(const std::string& key, const std::any& value,
Layer layer, NotifyPolicy notify_policy);
RegisterResult register_key(const Key& key, const std::any& init_value,
Layer layer, NotifyPolicy notify_policy);
UnRegisterResult unregister_key(const Key& key, Layer layer,
NotifyPolicy notify_policy);
// 持久化操作
void sync(bool async);
void reload();
// Watcher 操作
WatcherHandle watch(const std::string& pattern, Watcher callback,
NotifyPolicy policy);
void unwatch(WatcherHandle handle);
NotifyResult notify();
};
```text
**注意事项**:
1. 保持与现有 ConfigStoreImpl 相同的接口签名
2. 实现相同的线程安全保证(使用 shared_mutex)
3. 处理脏标记和持久化逻辑
4. 实现 Watcher 机制和延迟回调
### 5.4 自定义 NotifyPolicy
**当前实现**:
- `Manual`:累积变更,手动调用 `notify()` 触发
- `Immediate`:立即触发 Watcher
**扩展可能性**:
- `Batched`:按时间窗口批量触发
- `Debounced`:防抖,短时间多次变更只触发一次
- `Conditional`:基于条件判断是否触发
**实现位置**:
`cfconfig_notify_policy.h`
## 6. 性能考虑
### 6.1 缓存策略
**当前实现**:
- 查询时缓存命中值到 `cache_`
- Temp 层完全基于内存缓存
- 写入时同步更新缓存
**潜在优化**:
1. LRU 缓存限制大小
2. 延迟写入减少磁盘 I/O
3. 预加载热点配置
### 6.2 锁竞争优化
**当前措施**:
1. 读写分离(shared_mutex)
2. 延迟回调(锁外执行)
3. 细粒度锁(deferred_mutex_)
**进一步优化**:
1. 分片锁:按 key 哈希分片
2. 无锁结构:使用原子操作替代锁
3. 读写分离:CQRS 模式
### 6.3 持久化优化
**当前实现**:
- `sync()` 主动同步
- 析构时自动同步
- 脏标记避免无意义写入
**潜在优化**:
1. 异步写入:后台线程定期同步
2. 批量写入:累积变更后批量写入
3. 增量同步:仅同步变更部分
## 7. 故障处理
### 7.1 文件访问失败
**System 层**:
- 构造时检查文件存在性
- 不存在时跳过初始化
- 运行时返回空值
**User/App 层**:
- 自动创建目录(`QDir().mkpath()`)
- 写入失败返回 false
- 不抛出异常
### 7.2 类型转换失败
**策略**:返回默认值
- `any_cast` 失败时使用默认值
- 不中断程序流程
- 记录日志(需配合日志系统)
### 7.3 Watcher 异常
**当前设计**:
- 不捕获回调中的异常
- 异常向上传播
**改进建议**:
- 捕获并记录异常
- 继续执行其他 Watcher
- 提供错误回调机制
## 8. 调试支持
### 8.1 状态检查
```cpp
// 获取待通知变更数量
std::size_t pending = ConfigStore::instance().pending_changes();
// 检查键是否存在
bool exists = ConfigStore::instance().has_key(key_view);
// 检查特定层
bool exists_in_app = ConfigStore::instance().has_key(key_view, Layer::App);
```text
### 8.2 日志建议
建议在以下关键点添加日志:
1. 配置加载/保存
2. Watcher 触发
3. 路径提供者初始化
4. 错误和异常情况
### 8.3 测试支持
**路径提供者**:使用 MockPathProvider
```cpp
#include <cfconfig/cfconfig_path_provider.h>
class MockPathProvider : public cf::config::IConfigStorePathProvider {
public:
~MockPathProvider() override = default;
explicit MockPathProvider(const QString& base_dir)
: base_dir_(base_dir) {}
QString system_path() const override {
return base_dir_ + "/system.ini";
}
QString user_dir() const override {
return base_dir_ + "/user";
}
QString user_filename() const override {
return "user.ini";
}
QString app_dir() const override {
return base_dir_ + "/app";
}
QString app_filename() const override {
return "app.ini";
}
bool is_layer_enabled(int layer_index) const override {
return true; // 启用所有层
}
private:
QString base_dir_;
};
// 使用方式
auto mock_provider = std::make_shared<MockPathProvider>("/tmp/test_config");
cf::config::ConfigStore::instance().initialize(mock_provider);
```yaml
**单元测试**:参考 `test/config_manager/config_store_test.cpp`
## 9. 总结
ConfigStore 架构的核心优势:
1. **分层设计**:四层优先级实现灵活的配置覆盖
2. **Pimpl 模式**:接口稳定,实现可替换
3. **线程安全**:读写锁 + 延迟回调确保并发安全
4. **可扩展**:路径提供者、KeyHelper 等支持自定义
5. **性能优化**:缓存策略、锁分离、延迟执行
适用场景:
- 桌面应用程序配置管理
- 需要多层级配置的系统
- 多线程环境下的配置访问
- 需要配置变更通知的场景
---
文档版本:1.0
创建日期:2026-03-17
维护者:CFDesktop Team