服务器配置系统主要作用是为服务器提供便利的服务,体现约定大于配置的思想。比如为日志系统配置日志器、日志输出器、日志格式器等,省去了大量的定义语句。此处使用的配置文件为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);
        }
    }
}