sylar高性能服务器-配置(P10-p11)代码解析+调试分析

news2024/12/23 13:11:06

文章目录

    • p9:配置模块搭建
      • 一、ConfigvarBase
      • 二、ConfigVar
      • 三、Config
      • 四、小结
    • p10:YAML的使用
      • 一、安装yaml-cpp
      • 二、使用yaml-cpp
      • 三、代码解析
    • P11:YAML与日志的整合
      • 一、方法函数
      • 二、代码调试
      • 三、test_config结果
      • 四、小结

p9:配置模块搭建

​ 很长时间没写了,又把上次的断点p9重新看了一次。

​ 前面几个章节已经搭建好了日志系统的基本架构,从第九节开始搭建配置模块。该模块的主要用于定义/声明配置项,并且从配置文件中加载用户的配置。整个配置模块的目标就是与日志模块相结合,当配置文件相应参数做出改变时,能够通过回调函数改变相应的参数。

​ 配置模块包含3个类

  • ConfigvarBase:作为配置基类,放置一些公用的属性
  • ConfigVar:配置参数模板子类,主要功能实现stringT类型之间的相互转化
  • Config:ConfigVar的管理类

一、ConfigvarBase

​ 两个成员变量m_namem_description分别定义配置参数的名称和描述,纯虚函数toStringfromString由子类ConfigVar实现。

class ConfigvarBase {
public:
    typedef std::shared_ptr<ConfigvarBase> ptr;
    ConfigvarBase(const std::string name, const std::string description = "")
        :m_name(name)
        ,m_description(description) {}                                  // 构造函数
    virtual ~ConfigvarBase() {}                                         // 析构函数

    const std::string& getName() const { return m_name; }               // 返回配置参数名称
    const std::string& getDescription() const { return m_description; } // 返回配置参数描述
    virtual std::string toString() = 0;                                 // 转换成字符串
    virtual bool fromString(const std::string& val) = 0;                // 从字符串初始化值
protected:
    std::string m_name;                                                 // 配置参数的名称
    std::string m_description;                                          // 配置参数的描述
};

二、ConfigVar

m_val是参数名对应的参数值。从构造函数可以看出目前的配置类主要包括配置名称、参数值以及配置描述3个变量。toStringfromString成员函数主要使用lexical_cast进行了类型转换,如果转换失败会打印出日志、异常以及值的类型。

template<class T>
class ConfigVar : public ConfigvarBase {
public:
    typedef std::shared_ptr<ConfigVar> ptr;

    ConfigVar(const std::string& name, const T& default_value, const std::string& description = "") // 初始化配置名称、参数值以及参数描述
        :ConfigvarBase(name,description)
        ,m_val(default_value) {

        }
    std::string toString() override {   // 将参数值转换为string类型
        try {
            return boost::lexical_cast<std::string>(m_val);
        } catch(std::exception& e) {
            SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception"
                << e.what() << "convert: " << typeid(m_val).name() << " to string";
        }
        return "";
    }
    bool fromString(const std::string& val) override {  // 从string转换为参数值
        try {
           m_val = boost::lexical_cast<T>(val);
        } catch (std::exception& e) {
            SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::fromString exception "
                << e.what() << " convert: string to " << typeid(m_val).name();
        }
        return false;
    }

    const T getValue() const { return m_val; }
    void setValue(const T& v) { m_val = v; }

private:
    T m_val;
};

三、Config

Config是配置管理类,成员变量s_datas使用static是为了保证初始化顺序。成员函数Lookup则是查找目标配置项。

class Config {
public:
    typedef std::map<std::string, ConfigvarBase::ptr> ConfigVarMap;

    // 定义如果没有则初始化
    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name,
            const T& default_value, const std::string& description = "") { // typename这里是告诉编译器::后面是类型,而不是变量名
        auto tmp = Lookup<T>(name); 
        if(tmp) {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup nmae = " << name << " exists";
        }
        // 发现异常
        if(name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._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));
        s_datas[name] = v;
        return v;
    }
    // 查找
    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
        auto it = s_datas.find(name);
        if(it == s_datas.end()) { // 未找到
            return nullptr;
        }
        return std::dynamic_pointer_cast<ConfigVar<T>>(it->second); // 找到转换成智能指针
    }
private:
    static ConfigVarMap s_datas;
};  

四、小结

​ 以上就是整个配置类的一个初步架构,下面编写测试文件进行测试。

  1. 在tests文件夹创建test_config.cc

    #include <iostream>
    #include "../sylar/log.h"
    #include "../sylar/util.h"
    #include"../sylar/config.h"
    
    sylar::ConfigVar<int>::ptr g_int_value_config = 
        sylar::Config::Lookup("system.port", (int)8080, "system port");
    
    // sylar::ConfigVar<float>::ptr g_float_value_config = 
    //     sylar::Config::Lookup("system.value", (float)10.2f, "system value");
    
    int main(int argc, char** argv) {
        std::cout <<  g_int_value_config->getValue() << std::endl;
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT());
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_int_value_config->getValue(); // SYLAR_LOG_INFO刚刚报错是因为我在定义时getRoot没有加()进行调用
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_int_value_config->toString();
    
        // SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_float_value_config->getValue(); 
        // SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_float_value_config->toString();
        return 0;
    }
    
  2. 在CMakeLists.txt文件添加下列内容

    cmake_minimum_required(VERSION 2.8)
    project(sylar)
    
    set(CMAKE_VERBOSE_MAKEFILE ON) 
    set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -g -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function")
    
    set(LIB_SRC
        sylar/log.cc
        sylar/util.cc
        sylar/config.cc
        )
    
    add_library(sylar SHARED ${LIB_SRC})
    #add_library(sylar_static STATIC ${LIB_SRC})
    #SET_TARGET_PROPERTIES(sylar_static PROPERTIES OUTPUT_NAME "sylar")
    
    add_executable(test tests/test.cc)
    add_dependencies(test sylar)
    target_link_libraries(test sylar)
    
    
    add_executable(test_config tests/test_config.cc)
    add_dependencies(test_config sylar)
    target_link_libraries(test_config sylar)
    
    
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
    
    
  3. 终端输入make编译

    image-20231211154955848

  4. 终端输入bin/test_config运行生成的可执行文件

    image-20231211155010478

p10:YAML的使用

​ 本节内容主要是通过使用yaml-cpp库从yaml文件中读取配置信息,下面内容主要记录配置yaml的步骤以及对sylar使用yaml-cpp里面的方法进行一个简单解释。

一、安装yaml-cpp

  1. 从github上克隆仓库git clone https://github.com/jbeder/yaml-cpp.git,结果如下图所示,我是把克隆下来的仓库放在了项目外面,因为我们使用时不需要用到整个仓库所有的东西。

    image-20231212103652881

  2. 在克隆下来的仓库里面创建一个build文件夹,终端输入cd yaml-cpp mkdir build

  3. 进入创建的build文件夹。输入cmake -DBUILD_SHARED_LIBS=ON ..,注意后面两个点不能忽略。然后得到如下内容,其中画横线的3个so文件就是我们需要的。

    image-20231212103953880

    1. 最后开始安装,输入下列命令

      make
      sudo make install
      

二、使用yaml-cpp

​ sylar这节演示的测试用例就是把一个yaml文件的内容遍历到控制台,上一步我们已经完成了yaml-cpp的安装,下面还需要项目文件了进行一些配置。

  1. 按照sylar的目录结构,在bin文件下创建一个conf文件夹,然后再创建一个log.yml文件,内容如下:

    logs:
        - name: root
          level: info
          formatter: '%d%T%m%n'
          appenders:
              - type: FileLogAppender
                file: /root/Web-learning/sylar/root.txt
              - type: StdoutLogAppender
        - name: system
          level: info
          formatter: '%d%T%m%n'
          appenders:
              - type: FileLogAppender
                file: /root/Web-learning/sylar/system.txt
              - type: StdoutLogAppender
    
    
  2. 进入我们刚才克隆下来编译后的yaml-cpp,首先在build文件夹找到下图3个文件

    image-20231212104631112

    把它们复制到sylar项目lib文件夹下,如下图所示:

    image-20231212104716985

    然后再回到yaml-cpp文件夹,找到里面的一个include文件夹,也将它复制到sylar中,如下:

    image-20231212104818366

  3. 进入CMakeLists.txt,添加一些配置文件(每一句我都写了注释),如下:

    cmake_minimum_required(VERSION 2.8)
    project(sylar)
    
    set(CMAKE_VERBOSE_MAKEFILE ON) 
    set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -g -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function")
    
    include_directories(.) # 这个不知道什么意思
    include_directories(/root/Web-learning/sylar/include) # 这个路径需要按照你自己主机上include放的位置
    link_directories(/root/Web-learning/sylar/lib) # 同样,根据自己主机lib的位置,必须保证该lib文件夹下有你刚刚复制的3个yaml-cpp的so库
    
    find_library(YAMLCPP libyaml-cpp.a) # 待
    
    set(LIB_SRC
        sylar/log.cc
        sylar/util.cc
        sylar/config.cc
        )
    
    add_library(sylar SHARED ${LIB_SRC})
    #add_library(sylar_static STATIC ${LIB_SRC})
    #SET_TARGET_PROPERTIES(sylar_static PROPERTIES OUTPUT_NAME "sylar")
    
    add_executable(test tests/test.cc)
    add_dependencies(test sylar)
    target_link_libraries(test sylar)
    
    
    add_executable(test_config tests/test_config.cc)
    add_dependencies(test_config sylar)
    target_link_libraries(test_config sylar -L/root/Web-learning/sylar/lib -lyaml-cpp) # 待
    
    
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
    
    
  4. 书写测试用例,进入test_config.cc

    #include <iostream>
    #include "../sylar/log.h"
    #include "../sylar/util.h"
    #include"../sylar/config.h"
    #include"yaml-cpp/yaml.h"
    
    sylar::ConfigVar<int>::ptr g_int_value_config = 
        sylar::Config::Lookup("system.port", (int)8080, "system port");
    
    // sylar::ConfigVar<float>::ptr g_float_value_config = 
    //     sylar::Config::Lookup("system.value", (float)10.2f, "system value");
    
    void print_yaml(const YAML::Node& node, int level) {
        if(node.IsScalar()) {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ') 
                << node.Scalar() << " - " << node.Type() << " - " << level;
        } else if(node.IsNull()) {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')  << "NULL - " << node.Type() << " - " << level;
        } else if(node.IsMap()) {
            for(auto it = node.begin(); it != node.end(); ++ it) {
                SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')
                    << it->first << " - " << it->second.Type() << " - " << level;
                print_yaml(it->second, level + 1);
            }
        } else if(node.IsSequence()) {
            for(size_t i = 0; i < node.size(); ++ i) {
                SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')
                    << i << " - " << node[i].Type() << " - " << level;
                print_yaml(node[i], level + 1);
            }
        }
    }
    
    
    
    void test_yaml() {
        YAML::Node root = YAML::LoadFile("/root/Web-learning/sylar/bin/conf/log.yml");
        print_yaml(root, 0);
    }   
    
    int main(int argc, char** argv) {
        // std::cout <<  g_int_value_config->getValue() << std::endl;
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT());
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_int_value_config->getValue(); // SYLAR_LOG_INFO刚刚报错是因为我在定义时getRoot没有加()进行调用
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_int_value_config->toString();
    
        // SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_float_value_config->getValue(); 
        // SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_float_value_config->toString();
        test_yaml();
        return 0;
    }
    
  5. 结果展示

    image-20231212105203133

三、代码解析

​ 简单介绍一下本节中使用的YAML里面的一些方法函数

  • YAML::LoadFile():接收一个文件路径,载入目标yml文件

  • node.IsScalar():判断当前是否为标量(可以理解为常量),从如下YAMLtype.h中,可以看到作者定义了5中不同类型,当我们读取自定义yml文件时就会根据当前遍历的值去判断应该做什么操作。

    image-20231212140407824

  • node.Type():对应上诉NodeType中的下标值,不过按照sylar的测试用例结果感觉有点不对,比如对于log.ymllogs的输出,logs作为一个map,它的下标应该对应4,然而结果node.Type()输出的是3。(我理解错了,看最后一点

    image-20231212141324449

  • std::string(level * 4, ’ '):根据level的不同在输出前面加空格的数量,美化结果输出

  • 对于node.Type()我刚刚提出的疑问,输出的IsMap是我在代码添加判断里加的,有一个问题就是当遇到Map时,会进行遍历,而遍历的内容可能会出现其它类型,比如Scalar或则Sequence,然而我却在该Map的条件判断里打印输出都加上了IsMap,所以造成输出的type值与IsMap不匹配。所以代码是没问题,之前理解错了。

P11:YAML与日志的整合

​ 本节主要内容是YAML与日志文件的整合,强烈建议学习本节之前花两分钟了解yaml的格式。如果需要直接运行代码就看第三节。

一、方法函数

​ 在配置管理类Config中新增了两个成员函数LoadFromYamlLookupBase,分别用于从yml文件中载入配置内容和从配置内容中查找相应的目标项目。

LookupBase

​ 接收一个字符串参数,在配置管理容器s_datas中查询目标配置。s_datasConfigVarMap类型,它的定义也在下面。如果在容器中找到了目标,则返回一个ConfigvarBase智能指针,包含目标配置参数的名称和描述

ConfigvarBase::ptr Config::LookupBase(const std::string& name) {
    auto it = s_datas.find(name);
    return it == s_datas.end() ? nullptr : it->second;
}

typedef std::map<std::string, ConfigvarBase::ptr> ConfigVarMap;

LoadFromYaml

​ 代码解析我就直接放在注释中

void Config::LoadFromYaml(const YAML::Node& root) {
    // 定义一个list,装载的元素是<string, node>类型
    std::list<std::pair<std::string, const YAML::Node>> all_nodes;
    // root中可以获取到日志里面配置的内容,ListAllMember则是把里面的内容分解成一个个节点,细节后面会讲
    ListAllMember("", root, all_nodes);

    // 检验ListAllMember的遍历结果是否正确
    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);

        // 如果找到
        if(var) {
            if(i.second.IsScalar()) {
                var->fromString(i.second.Scalar());
            } else {
                std::stringstream ss;
                ss << i.second;
                var->fromString(ss.str());
            }
        }
    }
}

ListAllMember

​ 该函数包含3个参数

  • prefix:前缀,比如配置文件中的内容name:root,解析时prefix = name,sylar在调用时传入的是一个空串,为了便于理解,我传入了一个null,目的就是当遇到最外层的对象时,比如log.yml中的logs:,构造node时应该生成null : logs:
  • node:表示一个YAML类型节点,如果当前的前缀合法,就把<prefix,node>放入结果listoutput
  • output:存储结果容器

整个解析流程比较简单:

  1. 首先判断当前前缀是否合法
  2. 如果前缀合法则创建一个pair存入output
  3. 如果当前的node是一个对象,则继续遍历该对象里面的元素,递归调用ListAllMember
ListAllMember("null", root, all_nodes); // 调用,应用时null记得还原为空串

static void ListAllMember(const std::string& prefix,
                            const YAML::Node& node,
                            std::list<std::pair<std::string, const YAML::Node>>& output) {

    // 判断prefix的合法性
    if(prefix.find_first_not_of("abcdefghijklmnopqrstuvwxyz._012345678") != std::string::npos) {
         SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Prefixe name invalid " << prefix << " : " << node;
         return;
    }
    output.push_back(std::make_pair(prefix, node));
    // 如果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);
        }
    }
}

二、代码调试

​ 写完代码后还是不太理解ListAllMember针对log.yml构建节点的方式,然后通过gdb调试有了一定的收获,不会调试的可以看看前面的文章,我都写得非常详细,这里就不列出调试的步骤,直接用实际数据演示过程。

对于log.yml的数据(省略了一些),logs是一个Map,里面有两个数组,system是一个对象,也就是可以通过system.port获取相应元素的值

logs:
    - name: root
      level: info
      formatter: '%d%T%m%n'
      appenders:
          - type: FileLogAppender
            file: /root/Web-learning/sylar/root.txt
          - type: StdoutLogAppender
    - name: system
      level: info
      formatter: '%d%T%m%n'
      appenders:
          - type: FileLogAppender
            file: /root/Web-learning/sylar/system.txt
          - type: StdoutLogAppender
system:
    port: 9900
    value: 15

载入上诉文件,结果存储在all_nodes,打印出来后结果如下

# 第一次打印,i.first = "", i.second = log.yml所有内容
logs:
  - name: root
    level: info
    formatter: "%d%T%m%n"
    appenders:
      - type: FileLogAppender
        file: /root/Web-learning/sylar/root.txt
      - type: StdoutLogAppender
  - name: system
    level: info
    formatter: "%d%T%m%n"
    appenders:
      - type: FileLogAppender
        file: /root/Web-learning/sylar/system.txt
      - type: StdoutLogAppender
system:
  port: 9900
  value: 15
0
# 第二次打印,i.first = "logs", i.second = logs里面的元素
logs- name: root
  level: info
  formatter: "%d%T%m%n"
  appenders:
    - type: FileLogAppender
      file: /root/Web-learning/sylar/root.txt
    - type: StdoutLogAppender
- name: system
  level: info
  formatter: "%d%T%m%n"
  appenders:
    - type: FileLogAppender
      file: /root/Web-learning/sylar/system.txt
    - type: StdoutLogAppender
1
# 第三次打印,i.first = "system", i.second = system里面的元素
systemport: 9900
value: 15
2
# 第三次打印,i.first = "system", i.second = system里面的元素
system.port9900
3
# 第四次打印,i.first = "system.port", i.second = 9900
system.value15
4
# 第四次打印,i.first = "system.value", i.second = 15

# 对于每个i.first,使用LookupBase查找打印结果
var.first: system.port var.second:  9900
var.first: system.value var.second:  15

LoadFromYaml中检验结果是否正确时回去all_nodes中查找,只有在查到system.portsystem.value才能成功

三、test_config结果

​ 在运行之前需要更改CMakeLists.txt的内容,我放在下面了

CMakeLists.txt


cmake_minimum_required(VERSION 2.8)
project(sylar)

set(CMAKE_VERBOSE_MAKEFILE ON) 
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -g -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function")

include_directories(.)
include_directories(/root/Web-learning/sylar/include)
link_directories(/root/Web-learning/sylar/lib)

find_library(YAMLCPP yaml-cpp)
message("***", ${YAMLCPP})

set(LIB_SRC
    sylar/log.cc
    sylar/util.cc
    sylar/config.cc
    )

add_library(sylar SHARED ${LIB_SRC})
#add_library(sylar_static STATIC ${LIB_SRC})
#SET_TARGET_PROPERTIES(sylar_static PROPERTIES OUTPUT_NAME "sylar")

add_executable(test tests/test.cc)
add_dependencies(test sylar)
target_link_libraries(test sylar ${YAMLCPP})


add_executable(test_config tests/test_config.cc)
add_dependencies(test_config sylar)
# target_link_libraries(test_config sylar -L/root/Web-learning/sylar/lib -lyaml-cpp)
target_link_libraries(test_config sylar ${YAMLCPP})


SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

test_config

#include <iostream>
#include "../sylar/log.h"
#include "../sylar/util.h"
#include"../sylar/config.h"
#include"yaml-cpp/yaml.h"

sylar::ConfigVar<int>::ptr g_int_value_config = 
    sylar::Config::Lookup("system.port", (int)8080, "system port");

sylar::ConfigVar<float>::ptr g_float_value_config = 
    sylar::Config::Lookup("system.value", (float)10.2f, "system value");

void print_yaml(const YAML::Node& node, int level) {
    if(node.IsScalar()) {
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ') 
            << node.Scalar() << " - " << node.Type() << " - " << level;
    } else if(node.IsNull()) {
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')  << "NULL - " << node.Type() << " - " << level;
    } else if(node.IsMap()) {
        for(auto it = node.begin(); it != node.end(); ++ it) {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')
                << it->first << " - " << it->second.Type() << " - " << level;
            print_yaml(it->second, level + 1);
        }
    } else if(node.IsSequence()) {
        for(size_t i = 0; i < node.size(); ++ i) {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level * 4, ' ')
                << i << " - " << node[i].Type() << " - " << level;
            print_yaml(node[i], level + 1);
        }
    }
}



void test_yaml() {
    YAML::Node root = YAML::LoadFile("/root/Web-learning/sylar/bin/conf/log.yml");
    print_yaml(root, 0);
}

void test_config() {
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before:" << g_int_value_config->getValue(); 
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before:" << g_float_value_config->toString();
    YAML::Node root = YAML::LoadFile("/root/Web-learning/sylar/bin/conf/log.yml");
    sylar::Config::LoadFromYaml(root);
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "after:" << g_int_value_config->getValue(); 
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "after:" << g_float_value_config->toString();
}

int main(int argc, char** argv) {
    // test_yaml();
    test_config();
    return 0;
}

结果

​ 可以看到,通过yml文件重新更改了配置参数的值。

image-20231212211538497

四、小结

​ 总的来说,本节最关键的函数就是ListAllMember构建节点的过程,可以把它的作用简单理解为,想要构建诸如First = xx.xx Second = Val,获取时就可以使用xx.xx = val的形式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1305686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年全球软件开发大会(QCon广州站2023)-核心PPT资料下载

一、峰会简介 本次峰会包含&#xff1a;泛娱乐时代的边缘计算与通讯、稳定性即生命线、下一代软件架构、出海的思考、现代数据架构、AGI 与 AIGC 落地、大前端技术探索、编程语言实战、DevOps vs 平台工程、新型数据库、AIGC 浪潮下的企业出海、AIGC 浪潮下的效能智能化、数据…

联邦边缘学习中的知识蒸馏综述

联邦边缘学习中的知识蒸馏综述 移动互联网的快速发展伴随着智能终端海量用户数据的产生。如何在保护数据隐私的前提下,利用它们训练出性能优异的机器学习模型,一直是业界关注的难点。为此,联邦学习应运而生,它允许在终端本地训练并协同边缘服务器进行模型聚合来实现分布式机器…

C++字符串插入函数(insert)

1.在下标为n处插入y #include <iostream> #include <algorithm> #include <string> using namespace std;string x,y; int n;int main() {cin>>x>>y>>n;x.insert(n,y); //在下表为n处插入ycout<<x<<endl;return 0; }2…待续

十几个软件测试实战项目【外卖/医药/银行/电商/金融】

项目一&#xff1a;ShopNC商城 项目概况&#xff1a; ShopNC商城是一个电子商务B2C电商平台系统&#xff0c;功能强大&#xff0c;安全便捷。适合企业及个人快速构建个性化网上商城。 包含PCIOS客户端Adroid客户端微商城&#xff0c;系统PC后台是基于ThinkPHP MVC构架开发的跨…

【TiDB理论知识10】TiDB6.0新特性

新特性 Placement Rules in SQL 小表缓存 内存悲观锁 Top SQL TiDB Enterprise Manager 一 Placement Rules in SQL Placement Rules in SQL 之前会遇到的问题 比如 北京的业务需要访问 T2 和 T3表 &#xff0c;但是T3表的数据在纽约 纽约的业务需要问访T4 T5 T6表…

基于PaddleNLP的深度学习对文本自动添加标点符号(一)

前言 目前以深度学习对文本自动添加标点符号研究很少&#xff0c;已知的开源项目并不多&#xff0c;详细的介绍就更少了&#xff0c;但对文本自动添加标点符号又在古文识别语音识别上有重大应用。 基于此&#xff0c;本文开始讲解基于PaddleNLP的深度学习对文本自动添加标点符号…

js 有关递归简单介绍

递归&#xff1a;指调用自身的函数 重点&#xff1a;递归函数必须有可以终止递归调用的语句&#xff0c;否则会导致内存溢出 递归的性能不好&#xff0c;因为在递归终止前&#xff0c;JavaScript引擎会为每一次递归分配一块内存以存储栈帧&#xff0c;随着递归的深入&#xff…

【C语言】结构体实现位段

引言 对位段进行介绍&#xff0c;什么是位段&#xff0c;位段如何节省空间&#xff0c;位段的内存分布&#xff0c;位段存在的跨平台问题&#xff0c;及位段的应用。 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;…

力扣每日一题----2008. 出租车的最大盈利

这题我们是怎么思考的呢&#xff1f; 已知有乘客最多30000个&#xff0c;有最多100000个地点&#xff0c;那么通过算法时间复杂度&#xff0c;不可能是O(n^2), 那么我们就可以去看题目&#xff0c;题目又是最多盈利多少元&#xff1f;那么很容易联想到动态规划&#xff0c;并…

9:00面试,9:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到12月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40…

支持向量机模型(超详细,含案例代码)

什么是支持向量机&#xff1f; 支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种在机器学习领域中用于分类和回归分析的强大算法。它是一种监督学习算法&#xff0c;其目标是在特征空间中找到一个超平面&#xff0c;将不同类别的数据点分开&…

Axure元件的介绍使用与登录界面以及个人简历的绘制

目录 一.Axure元件介绍 1.1.简介 1.2.常见的元件 1.3.元件的操作 二.基本元件的使用 2.1.矩形和圆形 2.2.图片 2.3.文本元件 2.4.热区 2.5.线段元件 三.表单型元件的使用 3.1.文本框 3.2.文本域 3.3.下拉列表 3.4.列表框 3.5.单选按钮 3.6.复选框 四.菜单和表…

2007-2022年全国各省数字经济政策支持力度、ZF工作报告数据经济词频数据

2007-2022年全国各省数字经济政策支持力度、ZF工作报告数据经济词频数据, 1、时间&#xff1a;2007-2022年 2、范围&#xff1a;31省市 3、指标&#xff1a;行政区划代码、年份、地区、所属地域、文本总长度、仅中英文-文本总长度、文本总词频-全模式、文本总词频-精确模式、…

dockerfile基于NMP+wordpress创建镜像

内部通信地址 nginx 172.111.0.10 docker-nginx mysql 172.111.0.20 docker-mysql php 172.111.0.30 docker-php 1、创建单独目录 2、创建容器的自定义网络 docker network create --subnet172.111.0.0/16 --opt "com.docker.network.bridge.name"&quo…

19、命令模式(Command Pattern,不常用)

命令模式&#xff0c;将一个请求封装为一个对象&#xff08;命令&#xff09;&#xff0c;使发出请求的责任和执行请求的责任分割开&#xff0c;有效降低系统的耦合度。这样两者之间通过命令对象进行沟通&#xff0c;这样方便将命令对象进行储存、传递、调用、增加与管理。命令…

磁力计LIS2MDL开发(1)----轮询获取磁力计数据

磁力计LIS2MDL开发.1--轮询获取磁力计数据 概述视频教学样品申请源码下载通信模式速率生成STM32CUBEMX串口配置IIC配置CS设置串口重定向参考程序初始换管脚获取ID复位操作BDU设置设置速率启用偏移消除开启温度补偿设置为连续模式轮询读取数据主程序演示 概述 本文将介绍如何使…

明道云在戴斯克,从业务整合到高效管理的秘诀

内容来自演讲&#xff1a;付震东 | 北京戴斯克商务有限公司 &#xff5c;数字化负责人 摘要 本文介绍了明道云在戴斯克公司的应用以及数字化转型过程中所起的作用。戴斯克公司使用明道云后&#xff0c;实现了系统落地速度、工作效率、决策方法和工作意识的提升。通过不断抽象…

集成开发之如何用好明道云

内容来自演讲&#xff1a;张嵩 | 苏州睿能科技有限公司 | 公司负责人 摘要 这篇文章介绍了作者所在公司如何利用明道云进行集成开发&#xff0c;并分享了四个实际案例。在第一个数字化实验室项目中&#xff0c;该公司使用明道云取代现有的STARLIMS商业软件&#xff0c;并实现…

微表情检测(四)----SL-Swin

SL-Swin: A Transformer-Based Deep Learning Approach for Macro- and Micro-Expression Spotting on Small-Size Expression Datasets 在本文中&#xff0c;我们致力于解决从视频中检测面部宏观和微观表情的问题&#xff0c;并通过使用深度学习方法分析光流特征提出了引人注…

XGBoost和LightGBM时间序列预测对比(备忘)

XGBoost和LightGBM都是目前非常流行的基于决策树的机器学习模型&#xff0c;它们都有着高效的性能表现&#xff0c;但是在某些情况下&#xff0c;它们也有着不同的特点。 XGBoost和LightGBM简单对比 训练速度 LightGBM相较于xgboost在训练速度方面有明显的优势。这是因为Ligh…