此项目是根据sylar框架实现,是从零开始重写sylar,也是对sylar丰富与完善
项目地址:https://gitee.com/lzhiqiang1999/server-framework
项目介绍:实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括socket、http、servlet等,支持快速搭建HTTP服务器或WebSokcet服务器。
详细内容:日志模块,使用宏实现流式输出,支持同步日志与异步日志、自定义日志格式、日志级别、多日志分离等功能。线程模块,封装pthread相关方法,封装常用的锁包括(信号量,读写锁,自旋锁等)。IO协程调度模块,基于ucontext_t实现非对称协程模型,以线程池的方式实现多线程,多协程协同调度,同时依赖epoll实现了事件监听机制。定时器模块,使用最小堆管理定时器,配合IO协程调度模块可以完成基于协程的定时任务调度。hook模块,将同步的系统调用封装成异步操作(accept, recv, send等),配合IO协程调度能够极大的提升服务器性能。Http模块,封装了sokcet常用方法,支持http协议解析,客户端实现连接池发送请求,服务器端实现servlet模式处理客户端请求,支持单Reator多线程,多Reator多线程模式的服务器。
test.yaml
tcp:
connect:
timeout: 10000
// 约定的配置
ConfigVar<int>::ptr tcp_config = Config::Lookup("tcp.connect.timeout", 5000, "tcp connect timeout");
LOG_INFO(LOG_ROOT()) << "before: " << tcp_config->getValue(); // before: 5000
// 读取配置文件
YAML::Node root = YAML::LoadFile("test.yaml");
// 加载配置文件,并更改配置值
Config::LoadFromYaml(root);
LOG_INFO(LOG_ROOT()) << "after: " << tcp_config->getValue(); // after: 10000
class ConfigVarBase
{
public:
typedef std::shared_ptr<ConfigVarBase> ptr;
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 ~ConfigVarBase() {}
//get, set方法
virtual std::string toString() = 0; // 序列化,转换成string
virtual bool fromString(const std::string& val) = 0; /
protected:
std::string m_name; /// 配置的字段的名字
std::string m_description; /// 字段的描述
};
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;
std::string toString() override;
bool fromString(const std::string& val) override;
uint64_t addListener(on_change_cb cb);
private:
T m_val; /// 配置变量
std::map<uint64_t, on_change_cb> m_cbs; /// 回调函数,当配置有改变时,回调
RWMutexType m_mutex; /// 读写锁,读多写少
}
template, class ToStr = LexicalCast>
,其中FromStr
完成序列化(string to value);ToStr
完成反序列化(value to string)。/**
* @brief 类型转换模板类(F 源类型, T 目标类型)
*/
template<class F, class T>
class LexicalCast {
public:
/**
* @brief 类型转换
* @param[in] v 源类型值
* @return 返回v转换后的目标类型
* @exception 当类型不可转换时抛出异常
*/
T operator()(const F& v) {
return boost::lexical_cast<T>(v);
}
};
/**
* @brief 类型转换模板类片特化(YAML String 转换成 std::vector)
*/
template<class T>
class LexicalCast<std::string, std::vector<T> > {
public:
std::vector<T> operator()(const std::string& v) {
//"yaml string [1,2,3] 转换成yaml node"
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;
}
};
/**
* @brief 类型转换模板类片特化(std::vector 转换成 YAML String)
*/
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);
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)
*/
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 转换成 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 ToStr = LexicalCast>
。这里使用了模板偏特化,如果T是普通类型(int),会自动匹配到LexicalCast
;如果T是复杂类型(vector
),会自动匹配到LexicalCast>
。//T to string YAML string
std::string toString() override
{
try
{
RWMutexType::ReadLock lock(m_mutex);
return ToStr()(m_val); // ToStr()(m_val)是仿函数,实际上是因为我们重写了operator()
}
catch (std::exception& e)
{
LOG_ERROR(LOG_ROOT()) << "ConfigVar::toString exception"
<< e.what() << " convert: " << typeid(m_val).name() << " to string";
}
return "";
}
//YAML string to T
bool fromString(const std::string& val) override
{
try
{
setValue(FromStr()(val)); // FromStr()(val)是仿函数,实际上是因为我们重写了operator()
return true;
}
catch (std::exception& e)
{
LOG_ERROR(LOG_ROOT()) << "ConfigVar::fromString exception"
<< e.what() << " convert: string to" << typeid(m_val).name();
}
return false;
}
fromString()
时,会调用setValue()
,此时会根据新值和旧值,判断是否需要调用回调,完成配置更新typedef std::function<void (const T& old_value, const T& new_value)> on_change_cb; // 回调函数的类型
std::map<uint64_t, on_change_cb> m_cbs; /// 该配置的所有回调
// 添加监听器
uint64_t addListener(on_change_cb cb)
{
//每次由系统决定key值,再返回,用于delListener
static uint64_t s_idx = 0;
RWMutexType::WriteLock lock(m_mutex);
++s_idx;
m_cbs[s_idx] = cb;
return s_idx;
}
// 设置配置值
void setValue(const T& value)
{
{
RWMutexType::ReadLock lock(m_mutex);
if (value == m_val) return;
//只有当old 和 new不同时才回调
for (auto& i : m_cbs)
{
i.second(m_val, value);
}
}
RWMutexType::WriteLock lock(m_mutex);
m_val = value;
}
static ConfigVarMap s_datas
//管理类
class Config
{
public:
typedef RWMutex RWMutexType;
typedef std::map<std::string, ConfigVarBase::ptr> ConfigVarMap;
//查找一个配置变量,没找到就创建一个新的,放到s_datas
//name:A.B
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name, const T& default_value, const std::string& description = "");
//查找name
template<class T>
static typename ConfigVar<T>::ptr Lookup(const std::string& name);
//解析yaml文件,读取配置信息
static void LoadFromYaml(const YAML::Node& root);
//查看当前配置表的信息
static void Visit(std::function<void(johnsonli::ConfigVarBase::ptr)> cb);
private:
//查找name,返回ConfigVarBase::ptr
static ConfigVarBase::ptr LookupBase(const std::string& name);
private:
/**
* @brief 返回所有的配置项
*/
static ConfigVarMap& GetDatas() {
static ConfigVarMap s_datas;
return s_datas;
}
//如果是静态成员变量,可能对象会先创建出来,但是静态变量还没有初始化
static RWMutexType& GetMutex()
{
static RWMutexType s_mutex;
return s_mutex;
}
};
struct LogAppenderDefine
与struct LogDefine
。//LogAppender
struct LogAppenderDefine
{
int type = 0; //1 File, 2 Stdout
LogLevel::Level level = LogLevel::UNKNOW;
std::string formatter;
std::string file;
bool operator==(const LogAppenderDefine& oth) const
{
return type == oth.type
&& level == oth.level
&& formatter == oth.formatter
&& file == oth.file;
}
};
//Logger
struct LogDefine
{
std::string name;
LogLevel::Level level = LogLevel::UNKNOW;
std::vector<LogAppenderDefine> appenders;
bool operator==(const LogDefine& oth) const
{
return name == oth.name
&& level == oth.level
&& appenders == oth.appenders;
}
bool operator<(const LogDefine& oth) const
{
return name < oth.name;
}
};
struct LogDefine
表示日志器的配置信息,因此需要完成日志器的序列化与反序列化//LogDefine偏特化 YAML string to LogDefine
template<>
class LexicalCast<std::string, LogDefine>
{
public:
LogDefine operator()(const std::string& str){...}
};
//LogDefine偏特化 LogDefine to YAML string
template<>
class LexicalCast<LogDefine, std::string>
{
public:
std::string operator()(const LogDefine& i) {...}
};
ConfigVar<std::set<LogDefine>>::ptr g_log_defines = Config::Lookup("logs", std::set<LogDefine>(), "logs config");
// LoadFromYaml(YAML string to T) --> 在SetValue中观察者模式触发日志更改事件 --> 调用回调函数(添加、修改、删除)
struct LogIniter
{
LogIniter()
{
g_log_defines->addListener([] (const std::set<LogDefine>& old_val, const std::set<LogDefine>& new_val)
{
// 添加,new_val有,old_val没有
// 修改,new_val有,old_val有,但不相同
// 删除,new_val没有,old_val有
}
}
};
static LogIniter __log_init;
这里使用了一个方法,使得添加回调函数的执行在main函数之前。在类的构造函数中处理,声明静态变量,这样就会在main函数之前进行构造,完成处理。
logs:
- name: root
level: debug
appenders:
- type: FileLogAppender
level: debug
file: root.log
formatter: "%d%TF%T[%p]%T[%c]%T%f:%l%T%m%n"
- type: StdoutLogAppender
level: debug
formatter: "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
- name: system
level: debug
appenders:
- type: FileLogAppender
level: debug
file: root.log
formatter: "%d%TF%T[%p]%T[%c]%T%f:%l%T%m%n"
- type: StdoutLogAppender
level: debug
formatter: "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"