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

文章目录

    • 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_SRCsylar/log.ccsylar/util.ccsylar/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: rootlevel: infoformatter: '%d%T%m%n'appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/root.txt- type: StdoutLogAppender- name: systemlevel: infoformatter: '%d%T%m%n'appenders:- type: FileLogAppenderfile: /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_SRCsylar/log.ccsylar/util.ccsylar/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: rootlevel: infoformatter: '%d%T%m%n'appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/root.txt- type: StdoutLogAppender- name: systemlevel: infoformatter: '%d%T%m%n'appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/system.txt- type: StdoutLogAppender
system:port: 9900value: 15

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

# 第一次打印,i.first = "", i.second = log.yml所有内容
logs:- name: rootlevel: infoformatter: "%d%T%m%n"appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/root.txt- type: StdoutLogAppender- name: systemlevel: infoformatter: "%d%T%m%n"appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/system.txt- type: StdoutLogAppender
system:port: 9900value: 15
0
# 第二次打印,i.first = "logs", i.second = logs里面的元素
logs- name: rootlevel: infoformatter: "%d%T%m%n"appenders:- type: FileLogAppenderfile: /root/Web-learning/sylar/root.txt- type: StdoutLogAppender
- name: systemlevel: infoformatter: "%d%T%m%n"appenders:- type: FileLogAppenderfile: /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_SRCsylar/log.ccsylar/util.ccsylar/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.mzph.cn/news/217084.shtml

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

相关文章

18、责任链模式(Chain of Responsibility Pattern)

责任链模式&#xff0c;是用于避免请求发送者与多个请求处理者耦合在一起&#xff0c;让所有请求的处理者持有下一个对象的引用&#xff0c;从而将请求串联成一条链&#xff0c;在有请求发生时&#xff0c;可将请求沿着这条链传递&#xff0c;直到遇到该对象的处理器。用户只需…

华为或荣耀手机禁止强制升级鸿蒙系统的终极方法

需要有数据传输的usb线.打开usb调试模式. 进这个链接下载华为ADB一键卸载VS重装软件 按里面的视频说明,输入88 然后回车即可 https://download.csdn.net/download/viqecel/12161462

SVN版本回退

文章目录 SVN版本回退 SVN版本回退 一、revert to this version和revert to this version的区别&#xff1a; 基于4674版本执行"revert to this version"操作效果&#xff1a; 基于4674版本执行"revert changes from this version"操作效果&#xff1…

推荐开源项目-网络应用协议框架Socket.D

基于事件和语义消息流的网络应用协议 Socket.D 0 代码仓库地址1 该开源项目特点2 项目结构3 核心理念-协议帧Frame4 结束语 0 代码仓库地址 https://gitee.com/noear/socketd 1 该开源项目特点 代码风格优雅文档说明齐全测试用例非常人性化上手快&#xff0c;代码用例很多代…

JDBC链接MySQL,实现对Goods表的增删改查并封装JDBC

项目目录结构 数据库配置 1.创建goods表 2.创建goods实体 package homework.MyJDBC;public class Goods {private int id;private String gId;private String gName;private float gPrice;private int gNum;public int getId() {return id;}public void setId(int id) {this.i…

Qt之自定义QToolTip,去掉显示动画和隐藏延时

一.效果 先来看看Qt原生QToolTip的缺点: 1.当提示内容无变化时,弹窗无法移动。只能先传个空字符串强制弹窗隐藏,然后在新位置再传个字符串。 If the text is the same as the currently shown tooltip, the tip will not move. You can force moving by first hiding the t…

晶圆划片机助力LED陶瓷基板高效切割:科技提升产业新高度

博捷芯半导体划片机在LED陶瓷基板制造领域&#xff0c;晶圆划片机作为一种先进的切割工具&#xff0c;正在为提升产业效率和产品质量发挥重要作用。通过精确的切割工艺&#xff0c;晶圆划片机将LED陶瓷基板高效地切割成独立的芯片&#xff0c;为LED产业的快速发展提供了有力支持…

ViT:视觉 Transformer

ViT&#xff1a;视觉 Transformer 网络结构Transformer 编码器MLP 头CNN 和 Transformer 网络结构 Transformer 的优势&#xff1a;注意力机制相当于一个多标签检索系统&#xff0c;位置嵌入能知道每个单词的位置&#xff0c;而且适合并行。 尝试把 Transformer 迁移到视觉领…

鸿蒙(HarmonyOS)应用开发——简易版轮播图

简述 轮播图在应用中&#xff0c;已经很常见的展现方式。像uniapp、iview&#xff0c;viewUI等前端组件框架&#xff0c;都提供了轮播图组件。那么在harmonyOS中&#xff0c;如果要实现轮播&#xff0c;我们是使用swiper 组件 swiper组件 swiper 组件是一种容器组件。它提供…

jmeter接口自动化测试通过csv文件读取用例并执行测试

最近在公司测试中经常使用jmeter这个工具进行接口自动化&#xff0c;简单记录下~ 一、在csv文件中编写好用例 首先在csv文件首行填写相关参数&#xff08;可根据具体情况而定&#xff09;并编写测试用例。脚本可通过优先级参数控制执行哪些接口&#xff0c;通过端口参数同时执…

【WebRTC】【Unity】Unity Web RTC1-Unity中简单实现远程画面

【项目资源下载】 本篇配套直接打开可用的项目包地址&#xff0c;欢迎下载&#xff1a; https://download.csdn.net/download/weixin_41697242/88612084 【背景】 想要在Unity中实现实时远程桌面&#xff0c;找到了Render Streaming这个手段&#xff0c;本篇介绍相应的使用方…

三天精通Selenium Web 自动化 - Selenium(Java)环境搭建 (new)

0 背景 开发工具idea代码管理mavenjdk1.8webdriver chrome 1 chromedriver & chrome chromedriver和chrome要对应上&#xff1a; chomedriver下载地址&#xff1a;淘宝镜像 这里用的是 chromedriver88-0-4324-96.zipchrome下载地址&#xff1a;如何降级和安装旧版本的C…

代码随想录刷题题Day11

刷题的第十一天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C / Python Day11 任务 ● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代 1 二叉树理论基础 1.1 二叉树的种类 &#xff08;1&…

js基础:简介、变量与数据类型、流程循环控制语句、数组及其api

JS基础&#xff1a;简介、变量与数据类型、流程循环控制语句、数组及其api 一、简介 1、js概述 tip&#xff1a;JavaScript是什么&#xff1f; 有什么作用&#xff1f; JavaScript&#xff08;简称JS&#xff09;是一种轻量级的、解释性的编程语言&#xff0c;主要用于在网页…

React系列:实现子组件A->父组件-子组件B变量流传

🍁 作者:知识浅谈,CSDN博客专家,阿里云签约博主,InfoQ签约博主,华为云云享专家,51CTO明日之星 📌 擅长领域:全栈工程师、爬虫、ACM算法 💒 公众号:知识浅谈 🔥网站:vip.zsqt.cc React系列总结 🎈useState的使用 创建响应式变量的时候,在react是需要使用u…

手机网站支付有风险吗?

这是一个在当今数字化时代中&#xff0c;消费者们常常会提出的问题。随着科技的发展和移动设备的普及&#xff0c;越来越多的商家开始接受在线支付&#xff0c;这无疑为消费者提供了极大的便利。然而&#xff0c;与此同时&#xff0c;也伴随着一些潜在的风险。本文将探讨手机网…

0012Java程序设计-ssm医院预约挂号及排队叫号系统

文章目录 **摘** **要**目 录系统实现5.2后端功能模块5.2.1管理员功能模块5.2.2医生功能模块 开发环境 摘 要 网络的广泛应用给生活带来了十分的便利。所以把医院预约挂号及排队叫号管理与现在网络相结合&#xff0c;利用java技术建设医院预约挂号及排队叫号系统&#xff0c;实…

Nginx安装【保姆级别】

目录 1.Nginx开源版本安装&#x1f495;&#x1f495;&#x1f495; 2.传到linux&#x1f495;&#x1f495;&#x1f495; 3.在linux上安装 &#x1f495;&#x1f495;&#x1f495; 4.启动Nginx &#x1f495;&#x1f495;&#x1f495; 5.安装成系统服务 &#x1f495…

Dockerfile创建镜像INMP+wordpress

Dockerfile创建镜像INMPwordpress 需要哪些呢&#xff1a; Nginx 172.111.0.10 docker-nginx Mysql 172.111.0.20 docker-mysql PHP 172.111.0.30 docker-PHP 开始实验&#xff1a; 创建各级目录&#xff0c;他们各自的包和配置文件必须要在同一目录下才可以生效&…

Fiddler中AutoResponder的简单使用

AutoResponder&#xff0c;自动回复器&#xff0c;用于将 HTTP 请求重定向为指定的返回类型。 这个功能有点像是一个代理转发器&#xff0c;可以将某一请求的响应结果替换成指定的资源&#xff0c;可以是某个页面也可以是某个本地文件 1.使用 打开“Fiddler”&#xff0c;点击…