服务器配置系统主要作用是为服务器提供便利的服务,体现约定大于配置的思想。比如为日志系统配置日志器、日志输出器、日志格式器等,省去了大量的定义语句。此处使用的配置文件为yml,通过yaml-cpp库进行解析,并用片特化的方式为大量数据类型实现序列化与反序列化,同时用回调函数的机制使得配置生效。
ConfigVarBase基类
此类主要为各个配置项定义基类,主要包括配置项的名称以及描述,为子类定义的核心方法为toString()与fromString()。主要用于序列化与反序列化。m_name为配置名称,存储使用小写。
std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower
m_description为配置参数的描述
class ConfigVarBase { public: typedef std::shared_ptr<ConfigVarBase> ptr; /** * @brief 构造函数 * @param[in] name 配置参数名称[0-9a-z_.] * @param[in] description 配置参数描述 */ ConfigVarBase(const std::string& name, const std::string& description = "") :m_name(name) ,m_description(description) { std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower); } virtual std::string getTypeName() const = 0; /** * @brief 析构函数 */ virtual ~ConfigVarBase() {} /** * @brief 返回配置参数名称 */ const std::string& getName() const { return m_name;} /** * @brief 返回配置参数的描述 */ const std::string& getDescription() const { return m_description;} /** * @brief 转成字符串 */ virtual std::string toString() = 0; /** * @brief 从字符串初始化值 */ virtual bool fromString(const std::string& val) = 0; /** * @brief 返回配置参数值的类型名称 */ //virtual std::string getTypeName() const = 0; protected: /// 配置参数的名称 std::string m_name; /// 配置参数的描述 std::string m_description; };
ConfigVar模版类
ConfigVar类是继承ConfigVarBase,是一个模版类,使用模版类是因为配置项可能是多种类型的,比如int,float,map,vector甚至还可能是自定义类型,比如说对日志系统进行配置就需要对自定义的日志系统进行片特化。
在template中T表示不同类型的配置参数,class FromStr=LexicalCast<std::string,T>是反序列化的片特化代表,class ToStr=LexicalCast<T,std::string>是序列化的片特化代表。分别表示对不同类型的配置参数进行序列化与反序列化。
在toString()函数中进行ToStr()(m_val)调用表示对T类型的配置参数进行序列化转换,具体使用的模版序列化类需要自己定义。同理在fromString()函数中的FromStr()(val)将string转化为T类型的配置参数,同时在fromString()函数中对于转换而来的T类型配置参数直接用setValue存储在自身的m_val中,这也就表示读取到的配置参数正式存储了起来。
在ConfigVar类中定义了回调函数机制,使用function定义回调函数类型,并在类中使用map中用唯一的uint64_t来标识回调函数。回调机制定义在ConfigVar类中是为了单独为每一项定义回调函数。
使用addListener(),delListener(),getListener(),clearListener()来管理回调函数。而回调函数真正调用是在setvalue函数中,表明在m_val更新的同时,也调用回调函数去通知配置更新。
typedef std::function<void (const T& old_value, const T& new_value)> on_change_cb; std::map<uint64_t, on_change_cb> m_cbs; sylar::ConfigVar<std::set<LogDefine> >::ptr g_log_defines = sylar::Config::Lookup("logs", std::set<LogDefine>(), "logs config");//添加logs配置选项。 g_log_defines->addListener([](const std::set<LogDefine>& old_value, const std::set<LogDefine>& new_value){ std::cout<<"add logs"<<std::end; })//为logs配置选项添加回调函数,可以使得m_val中的参数设置到loggerManager中去,也就使得配置真正生效了。
template<class T,class FromStr=LexicalCast<std::string,T>,class ToStr=LexicalCast<T,std::string> > class ConfigVar : public ConfigVarBase { public: // typedef RWMutex RWMutexType; typedef std::shared_ptr<ConfigVar> ptr; typedef std::function<void (const T& old_value, const T& new_value)> on_change_cb; /** * @brief 通过参数名,参数值,描述构造ConfigVar * @param[in] name 参数名称有效字符为[0-9a-z_.] * @param[in] default_value 参数的默认值 * @param[in] description 参数的描述 */ ConfigVar(const std::string& name ,const T& default_value ,const std::string& description = "") :ConfigVarBase(name, description) ,m_val(default_value) { } /** * @brief 将参数值转换成YAML String * @exception 当转换失败抛出异常 */ std::string toString() override { try { //return boost::lexical_cast<std::string>(m_val); // RWMutexType::ReadLock lock(m_mutex); return ToStr()(m_val); } catch (std::exception& e) { SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception " << e.what() << " convert: " //<< TypeToName<T>() << " to string" << " name=" << m_name; } return ""; } /** * @brief 从YAML String 转成参数的值 * @exception 当转换失败抛出异常 */ bool fromString(const std::string& val) override { try { //m_val=boost::lexical_cast<T>(val); //std::cout<<"yes1199911 "<<val<<" "<<m_val<<std::endl; setValue(FromStr()(val)); std::cout<<"yes1199911 "<<this->toString()<<std::endl; } catch (std::exception& e) { // std::cout<<"yes11311ooooo"<<std::endl; SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception " << e.what() << " convert: string to " //<< TypeToName<T>() << " name=" << m_name << " - " << val; } return false; } /** * @brief 获取当前参数的值 */ const T getValue() { // RWMutexType::ReadLock lock(m_mutex); return m_val; } /** * @brief 设置当前参数的值 * @details 如果参数的值有发生变化,则通知对应的注册回调函数 */ void setValue(const T& v) { { // RWMutexType::ReadLock lock(m_mutex); if(v == m_val) { std::cout<<"woqu "<<this->toString()<<std::endl; return; } for(auto& i : m_cbs) { i.second(m_val, v); } } // RWMutexType::WriteLock lock(m_mutex); m_val = v; } /** * @brief 添加变化回调函数 * @return 返回该回调函数对应的唯一id,用于删除回调 */ uint64_t addListener(on_change_cb cb) { static uint64_t s_fun_id = 0; // RWMutexType::WriteLock lock(m_mutex); ++s_fun_id; m_cbs[s_fun_id] = cb; return s_fun_id; } /** * @brief 删除回调函数 * @param[in] key 回调函数的唯一id */ void delListener(uint64_t key) { // RWMutexType::WriteLock lock(m_mutex); m_cbs.erase(key); } /** * @brief 获取回调函数 * @param[in] key 回调函数的唯一id * @return 如果存在返回对应的回调函数,否则返回nullptr */ on_change_cb getListener(uint64_t key) { // RWMutexType::ReadLock lock(m_mutex); auto it = m_cbs.find(key); return it == m_cbs.end() ? nullptr : it->second; } /** * @brief 清理所有的回调函数 */ void clearListener() { // RWMutexType::WriteLock lock(m_mutex); m_cbs.clear(); } std::string getTypeName() const override { //return TypeToName<T>(); return ""; } private: // RWMutexType m_mutex; T m_val; std::map<uint64_t, on_change_cb> m_cbs; };
片特化模版类定义
这些类使得在Configvar中序列化与反序列化生效。
template<class F,class T> class LexicalCast{ public: T operator () (const F & v){ return boost::lexical_cast<T>(v); } }; template<class T> class LexicalCast<std::string,std::vector<T> >{ public: std::vector<T> operator () (const std::string & v){ YAML::Node node = YAML::Load(v); typename std::vector<T> vec; std::stringstream ss; for(size_t i=0;i<node.size();i++){ ss.str(""); ss<<node[i]; vec.push_back(LexicalCast<std::string,T>()(ss.str())); } return vec; } }; template<class T> class LexicalCast<std::vector<T>,std::string>{ public: std::string operator () (const std::vector<T> & v){ YAML::Node node(YAML::NodeType::Sequence); std::stringstream ss; for(auto& i : v) // for(size_t i=0;i<v.size();i++) { node.push_back(YAML::Load(LexicalCast<T, std::string>()(i))); // node.push_back(YAML::Load(LexiCalCast<T,std::string>()(v[i]))); } ss<<node; return ss.str(); } }; template<class T> class LexicalCast<std::string,std::list<T> >{ public: std::list<T> operator () (const std::string & v){ YAML::Node node = YAML::Load(v); typename std::list<T> vec; std::stringstream ss; for(size_t i=0;i<node.size();i++){ ss.str(""); ss<<node[i]; vec.push_back(LexicalCast<std::string,T>()(ss.str())); } return vec; } }; template<class T> class LexicalCast<std::list<T>,std::string>{ public: std::string operator () (const std::list<T> & v){ YAML::Node node(YAML::NodeType::Sequence); std::stringstream ss; for(auto & i: v) //for(size_t i=0;i<v.size();i++) { node.push_back(YAML::Load(LexicalCast<T, std::string>()(i))); // node.push_back(YAML::Load(LexicalCast<T,std::string>()(v[i]))); } ss<<node; return ss.str(); } }; template<class T> class LexicalCast<std::string,std::set<T> >{ public: std::set<T> operator () (const std::string & v){ YAML::Node node = YAML::Load(v); typename std::set<T> vec; std::stringstream ss; for(size_t i=0;i<node.size();i++){ ss.str(""); ss<<node[i]; vec.insert(LexicalCast<std::string,T>()(ss.str())); } return vec; } }; template<class T> class LexicalCast<std::set<T>,std::string>{ public: std::string operator () (const std::set<T> & v){ YAML::Node node(YAML::NodeType::Sequence); std::stringstream ss; // for(size_t i=0;i<v.size();i++) for(auto& i: v) { node.push_back(YAML::Load(LexicalCast<T, std::string>()(i))); // node.push_back(LexiCalCast<T,std::string>()(i)); } ss<<node; return ss.str(); } }; template<class T> class LexicalCast<std::string,std::unordered_set<T> >{ public: std::unordered_set<T> operator() (const std::string & v){ YAML::Node node=YAML::Load(v); std::stringstream ss; std::unordered_set<T> vec; for(size_t i=0;i<node.size();i++) { ss.str(""); ss<<node[i]; vec.insert(LexicalCast<std::string,T>()(ss.str())); } return vec; } }; template<class T> class LexicalCast<std::unordered_set<T>, std::string> { public: std::string operator()(const std::unordered_set<T>& v) { YAML::Node node(YAML::NodeType::Sequence); for(auto& i : v) { node.push_back(YAML::Load(LexicalCast<T, std::string>()(i))); } std::stringstream ss; ss << node; return ss.str(); } }; /** * @brief 类型转换模板类片特化(YAML String 转换成 std::map<std::string, T>) */ template<class T> class LexicalCast<std::string, std::map<std::string, T> > { public: std::map<std::string, T> operator()(const std::string& v) { YAML::Node node = YAML::Load(v); typename std::map<std::string, T> vec; std::stringstream ss; for(auto it = node.begin(); it != node.end(); ++it) { ss.str(""); ss << it->second; vec.insert(std::make_pair(it->first.Scalar(), LexicalCast<std::string, T>()(ss.str()))); } return vec; } }; /** * @brief 类型转换模板类片特化(std::map<std::string, T> 转换成 YAML String) */ template<class T> class LexicalCast<std::map<std::string, T>, std::string> { public: std::string operator()(const std::map<std::string, T>& v) { YAML::Node node(YAML::NodeType::Map); for(auto& i : v) { node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second)); } std::stringstream ss; ss << node; return ss.str(); } }; template<class T> class LexicalCast<std::string,std::unordered_map<std::string,T> >{ public: std::unordered_map<std::string,T> operator() (const std::string& v){ YAML::Node node=YAML::Load(v); typename std::unordered_map<std::string,T> vec; std::stringstream ss; for(auto it=node.begin();it!=node.end();it++){ ss.str(""); ss<<it->second; vec.insert(std::make_pair(it->first.Scalar(),LexicalCast<std::string, T>()(ss.str()))); // vec[it->first]=LexicalCast<std::string,T>(ss.str()); } return vec; } }; template<class T> class LexicalCast<std::unordered_map<std::string,T>,std::string> { public: std::string operator () (const std::unordered_map<std::string,T>& v) { YAML::Node node(YAML::NodeType::Map); for(auto& i:v){ node[i.first] =YAML::Load(LexicalCast<T, std::string>()(i.second)); } std::stringstream ss; ss << node; return ss.str(); } };
Config管理类
Config类对配置参数ConfigVar类进行统一管理,使用静态函数的方式去定义静态map来管理ConfigVar类,并用配置项名称来唯一标识。使用GetDatas()内部来定义ConfigVarMap类型,是为了防止其他静态类型调用s_datas时还未定义,所以写在函数里保证了s_datas在获取时一定定义了。
typedef std::unordered_map<std::string, ConfigVarBase::ptr> ConfigVarMap; static ConfigVarMap& GetDatas() { static ConfigVarMap s_datas; return s_datas; }
两个 LookUp函数分别在s_datas中添加以及查找对应的配置类。在第一个LookUp中,首先在s_datas中查找是否存在,如果存在看添加的类型是否与ConfigVar中的类型一致,如果一致则告知已经存在,如果不一致则输出错误。如果在s_datas中不存在则判断在配置类名称中是否存在不符合定义字符,如果通过则进行在s_datas添加。在第二个LookUp中只是用配置名在s_datas中进行查找,如果存在则返回智能指针。
class Config { public: typedef std::unordered_map<std::string, ConfigVarBase::ptr> ConfigVarMap; // typedef RWMutex RWMutexType; /** * @brief 获取/创建对应参数名的配置参数 * @param[in] name 配置参数名称 * @param[in] default_value 参数默认值 * @param[in] description 参数描述 * @details 获取参数名为name的配置参数,如果存在直接返回 * 如果不存在,创建参数配置并用default_value赋值 * @return 返回对应的配置参数,如果参数名存在但是类型不匹配则返回nullptr * @exception 如果参数名包含非法字符[^0-9a-z_.] 抛出异常 std::invalid_argument */ template<class T> static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "") { // RWMutexType::WriteLock lock(GetMutex()); auto it = GetDatas().find(name); if(it != GetDatas().end()) { auto tmp = std::dynamic_pointer_cast<ConfigVar<T> >(it->second); if(tmp) { SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists"; return tmp; } else { SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists but type not "; // << TypeToName<T>() << " real_type=" << it->second->getTypeName() // << " " << it->second->toString(); return nullptr; } } if(name.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") != std::string::npos) { SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name; throw std::invalid_argument(name); } typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description)); GetDatas()[name] = v; return v; } /** * @brief 查找配置参数 * @param[in] name 配置参数名称 * @return 返回配置参数名为name的配置参数 */ template<class T> static typename ConfigVar<T>::ptr Lookup(const std::string& name) { // RWMutexType::ReadLock lock(GetMutex()); auto it = GetDatas().find(name); if(it == GetDatas().end()) { return nullptr; } return std::dynamic_pointer_cast<ConfigVar<T> >(it->second); } /** * @brief 使用YAML::Node初始化配置模块 */ static void LoadFromYaml(const YAML::Node& root); /** * @brief 查找配置参数,返回配置参数的基类 * @param[in] name 配置参数名称 */ static ConfigVarBase::ptr LookupBase(const std::string& name); static void ALL(){ auto s=GetDatas(); for(auto & i: s){ std::cout<<i.first<<std::endl; } } private: /** * @brief 返回所有的配置项 */ static ConfigVarMap& GetDatas() { static ConfigVarMap s_datas; return s_datas; } };
在Config类中LoadFromYaml函数可以对yml文件进行解析,由于遵守约定大于配置的原则,如果解析出来的配置选项在s_data中存在的话,就用fromstring()对相应ConfigVar进行更改,同时在fromstring()中用setValue进行更改存储时,会调用这一项配置参数已经注册的回调函数,从而使得配置真正生效。
ConfigVarBase::ptr Config::LookupBase(const std::string& name) { // RWMutexType::ReadLock lock(GetMutex()); auto it = GetDatas().find(name); return it == GetDatas().end() ? nullptr : it->second; } void Config::LoadFromYaml(const YAML::Node& root) { std::list<std::pair<std::string, const YAML::Node> > all_nodes; ListAllMember("", root, all_nodes); std::cout<<"jiexidaxiao "<<all_nodes.size()<<std::endl; for(auto& i : all_nodes) { std::string key = i.first; if(key.empty()) { continue; } std::transform(key.begin(), key.end(), key.begin(), ::tolower); ConfigVarBase::ptr var = LookupBase(key); std::cout<<key<<std::endl; if(var) { std::cout<<"yes11111i "<<key<<std::endl; if(i.second.IsScalar()) { std::cout<<"yes11311 "<<i.second.Scalar()<<std::endl; var->fromString(i.second.Scalar()); } else { std::cout<<"yes12111"<<std::endl; std::stringstream ss; ss << i.second; std::cout<<"oooo"<<ss.str()<<std::endl; var->fromString(ss.str()); } } } }
ListAllMember()函数在LoadFromYaml函数中进行调用,对Yml文件进行递归的解析,此处只对YAML::NodeType::MAP类型的YAML::Node进行递归处理,对于YAML::NodeType::Sequence不进行递归。
//"A.B", 10 //A: // B: 10 // C: str static void ListAllMember(const std::string& prefix, const YAML::Node& node, std::list<std::pair<std::string, const YAML::Node> >& output) { if(prefix.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678") != std::string::npos) { SYLAR_LOG_ERROR(g_logger) << "Config invalid name: " << prefix << " : " << node; return; } output.push_back(std::make_pair(prefix, node)); if(node.IsMap()) { for(auto it = node.begin(); it != node.end(); ++it) { ListAllMember(prefix.empty() ? it->first.Scalar() : prefix + "." + it->first.Scalar(), it->second, output); } } }