Skip to content

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

Built with VitePress