定时更新配置的C++实现

最近的几个系统都用到了定时更新配置

获取到的配置是需要高频使用的,不能直接使用字符串

需要预处理为整数或浮点数,甚至是一个整数数组

因此整理了一下Config设计:

分为元数据Meta,配置的存储和注册Config两个模块

Meta由用户来指定反射所需类型信息Config定义

Config是定时拉取远程的信息,用来更新本地的变量

Meta的职责

  • 在Config中定位配置项的地址
  • 配置默认值或拿到的字符串如何转化为值的lambda
    • 这还提供了数据类型的信息

Config的职责

  • 存储配置的实际数据
  • 关联存储和元数据,将元数据注册进来,在外部调用updateConfig时更新所有关联元数据所存储的数据

类图如下

Meta还包含了一个lambda特化

在Config和Meta以外

  • Value用于数据的存储,封装在Config类内不对外暴露
  • ConfigHelper用于使用Config和Meta获取数据
classDiagram
direction LR
class MetaValue["Meta<T>"] {
	+const char* key;
	+T defaultValue;
}
class MetaFunc["template &lt;typename Ret, typename... Args><br>Meta&lt;function&lt;Ret(Args...) noexcept>>"] {
	+const char* key;
	+function&lt;Ret[Args...] noexcept> func
}
class ValueBase["ValueBase&lt;InputConfigDataT>"] {
	+virtual parse(const InputConfigDataT &)* void
}
class Value["Value&lt;InputConfigDataT, T><br> : public ValueBase&lt;InputConfigDataT>"] {
	+Value(const function&lt;T[const InputConfigDataT&]>&) //初始化parseFunc_
	+parse(const InputConfigDataT&) void // override,使用注册的parseFunc_解析出值存储到value_
	+getPtr() shared_ptr~T~ //使用atomic_load读取值
	-setPtr(shared_ptr~T~ &) //使用atomic_store存储值
	-shared_ptr~T~ value_;
	-function&lt;T[const InputConfigDataT&]> parseFunc_;
}
Value ..|> ValueBase
class Config["Config&lt;InputConfigDataT>"] {
	+virtual register()* void
	#registerMeta(Meta~T~) void //实现了注册Meta的接口,用来override的register中调用
	+updateConfig(const InputConfigDataT& data) //用于定时更新Config,这里对存储的values_依次调用parse(data)接口来存储数据
	-map&lt;const char*, ValueBase&lt;InputConfigDataT>> values_;
}
Config *-- ValueBase
Config ..> MetaValue
class ConfigHelper {
	+getPtr(const Config~InputConfigData~ &, const Meta~T~&)$void //将Config中values_存储的数据提取出来
}
ConfigHelper ..> Config
ConfigHelper ..> MetaValue
MetaFunc ..|> MetaValue

使用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//in LeafConfigClient.hpp
class LeafConfigClient {
void run() {
while(!stop) {
runOnce();
}
}
void runOnce() {
TC_Config c;
//fetch config from server
config_.updateConfig(c);
}
Config<TC_Config> config_;
template <typename MetaT>
auto getPtr(const MetaT &m) {
return ConfigHelper::getPtr(config_, m);
}
}

static Meta configA1{"configA1", 1};
static Meta configA2{"configA2", [](const TC_Config& c) {
return atoi(c["configA2"].c_str());
}};

//业务逻辑代码
auto v = *client.getPtr(configA);

单例数据可以再封装一层

1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename InputConfigDataT, typename MetaT>
class MetaHelper {
MetaT m_;
const Config<InputConfigDataT> &config_;
auto getPtr() {
return ConfigHelper::getPtr(config_, m_);
}
};

static MetaHelper configB{"configB", 1, LeafConfigClient::config_);

//业务逻辑代码
auto v = *configB.getPtr();