1.前言
前面花了一些时间把需要用到的技术进行了简单的讲解,把需要的环境也做了部署和安装。但是现在只是一个零散的代码,并不是一个整体的项目。后面就要开始正式的去写一个服务器了,所以我们需要把这些零散的东西做一个整理。
2.设计思路
①写一个线程基类,所有的工具都继承于这个线程基类,这样可以通过保存线程基类的指针,完成对所有工具的统一管理。
②把需要异步操作的工具进行封装,使之可以进行异步操作,这些工具包括timer mysql redis socket log等。
③创建一个def.h,用来保存宏定义和一些通用类型的定义。
④设计一个进程基类,把这些工具放在同一个进程。
3.开始整理
这里先分享一下我的代码
链接:https://pan.baidu.com/s/1-RrbPquu_g9Wbnedh2fe2g
提取码:vqg7
①在工作目录下创建一个lib目录,一个include目录
②将mysql和protobuf需要用到的动态链接库和头文件分别放在lib和include里面
③将Makefile的绝对路径改为相对路径
④将run.sh的绝对路径改为相对路径
⑤创建一个 XInclude目录用来保存封装好的工具
⑥创建一个XProcess目录用来保存进程的子类(进程的具体逻辑)
⑦我把代码打一个完整的包,大家可以下载下来看看
链接:https://pan.baidu.com/s/1-RrbPquu_g9Wbnedh2fe2g
提取码:vqg7
4.代码的思路
①封装一个线程类XThraed.h和XThread.cpp
/**********************************************************
* Author : 谢名聪
* Email : 869408604@qq.com
* Last modified : 2022-04-21 11:43
* Filename : XThread.h
* Description : 异步线程类
* *******************************************************/
#ifndef X_THREAD_H
#define X_THREAD_H
#include <atomic>
#include <future>
#include <chrono>
class XThread
{
public:
void run();
void stop();
bool status();
private:
virtual bool exec() = 0;//当状态是false的时候结束线程
private:
bool m_status = false;
std::future<bool> m_future;
};
#endif
#include "XThread.h"
void XThread::run()
{
m_status = true;
m_future = std::async(std::launch::async, std::bind(&XThread::exec, this));
}
void XThread::stop()
{
m_status = false;
}
bool XThread::status()
{
return m_status;
}
②让mysql继承这个线程,后面可以开发异步mysql操作
/**********************************************************
* Author : 谢名聪
* Email : 869408604@qq.com
* Last modified : 2022-04-21 11:45
* Filename : XMysql.h
* Description : xsqly类,提供同步异步两种操作
* *******************************************************/
#ifndef X_MYSQL
#define X_MYSQL
#include "../XThread/XThread.h"
#include "mysqlToProtobuf.h"
#include "/usr/local/mysql/include/mysql.h"
#include <iostream>
#include <vector>
struct XMysqlRes
{
bool suc = false;
std::vector<std::vector<std::string>> res;
};
class XMysql : public XThread
{
public:
bool exec() override {};
public:
//初始化数据库
bool init(const char* host, const char* port, const char* user, const char* pwd, const char* db, const uint32_t timeout = 2);
public:
//有返回的操作
XMysqlRes exeSqlStore(const char* sql);
//无返回的操作
bool exeSql(const char* sql);
//定义一个模板,针对不同的protobuf结构
template<typename PROTO_TYPE>
//这里必须再类内实现,在类外实现的话,进行外部链接的时候会出错
bool exeSqlProtobuf(const char* sql, std::vector<std::shared_ptr<PROTO_TYPE>>& protoRes)
{
if (!exeSql(sql)) {
std::cout << "XMysql exeSqlStore error! sql=" << sql << std::endl;
return false;
}
//转成protobuf形式
mysqlToProtobuf(mysql_store_result(&m_mysql), protoRes);
return true;
}
//校验连接状态
void check();
//连接数据库
bool connect();
private:
MYSQL m_mysql;
std::string m_host = "";
std::string m_user = "";
std::string m_pwd = "";
std::string m_db = "";
uint32_t m_port = 0;
uint32_t m_timeout = 0;
};
#endif
#include "XMysql.h"
bool XMysql::init(const char* host, const char* port, const char* user, const char* pwd, const char* db, const uint32_t timeout)
{
m_host = host;
m_port = std::atoi(port);
m_user = user;
m_pwd = pwd;
m_db = db;
m_timeout = timeout;
if (connect()) {
std::cout << "XMysql connect error" << std::endl;
return false;
}
return true;
}
bool XMysql::exeSql(const char* sql)
{
if (mysql_real_query(&m_mysql,sql,strlen(sql)) != 0) {
std::cout << "XMysql exeSql error! sql=" << sql << std::endl;
return false;
}
return true;
}
XMysqlRes XMysql::exeSqlStore(const char* sql)
{
XMysqlRes res;
check();
if (!exeSql(sql)) {
std::cout << "XMysql exeSqlStore error! sql=" << sql << std::endl;
return res;
}
//执行成功
res.suc = true;
//查询结果
auto result = mysql_store_result(&m_mysql);
//行数
auto rowNum = mysql_num_rows(result);
//列数
auto fiedNum = mysql_num_fields(result);
for (int i = 0; i < rowNum; i++) {
auto row = mysql_fetch_row(result);
std::vector<std::string> rows;
for (int j = 0; j < fiedNum; j++) {
rows.push_back(row[j]);
}
res.res.push_back(rows);
}
return res;
}
void XMysql::check()
{
//ping返回0时表示正常
if (mysql_ping(&m_mysql) != 0) {
std::cout << "XMysql ping == 0" << std::endl;
//关闭mysql
mysql_close(&m_mysql);
return;
}
//重新连接
connect();
}
bool XMysql::connect()
{
//初始化
if (!mysql_init(&m_mysql)) {
std::cout << "XMysql init error!" << std::endl;
return false;
}
//连接数据库
if (!mysql_real_connect(&m_mysql, m_host.c_str(), m_user.c_str(), m_pwd.c_str(), m_db.c_str(), m_port, NULL, CLIENT_MULTI_STATEMENTS)) {
std::cout << "XMysql connect error!" << std::endl;
return false;
}
//设置参数
mysql_options(&m_mysql, MYSQL_OPT_READ_TIMEOUT, &m_timeout);
mysql_options(&m_mysql, MYSQL_SET_CHARSET_NAME, "utf8");
}
③封装一个进程类Process
/**********************************************************
* Author : 谢名聪
* Email : 869408604@qq.com
* Last modified : 2022-04-21 11:42
* Filename : Process.h
* Description : 进程的基类
* *******************************************************/
#ifndef PROCESS_H
#define PROCESS_H
#include "XInclude/XMysql/XMysql.h"
#include "XInclude/XConfig/XConfig.h"
class Process
{
public:
Process();
virtual ~Process();
//进程子类需要实现的逻辑
private:
virtual bool initProcess() = 0;
virtual bool startProcess() = 0;
virtual bool stopProcess() = 0;
//main.cpp中调用的逻辑
public:
bool init(const std::string config);
bool start();
bool stop();
//*********************************************************
//工具类:包括config socket redis mysql timer log等
//*********************************************************
//---------------------------------------------------------
//config
//--------------------------------------------------------
private:
std::shared_ptr<XConfig> m_config;
public:
//获取配置
std::string getConfigValue(const std::string groupKey, const std::string key);
//-----------------------------------------------------------
//同步Mysql
//-----------------------------------------------------------
private:
//同步数据库,只操作一些简单的逻辑
std::map<uint32_t, std::shared_ptr<XMysql>> m_XMysqls;
public:
//增加数据库服务
bool addMysqlServer(uint32_t id, const char* host, \
const char* port, const char* user, \
const char* pwd, const char* db, \
uint32_t timeout = 2);
//无返回值
bool exeSql(uint32_t id, const char* sql);
//返回XMysql
XMysqlRes exeSqlStore(uint32_t id, const char* sql);
//返回protobuf
template<typename PROTO_TYPE>
bool exeSqlProtobuf(uint32_t id, const char* sql, std::vector<std::shared_ptr<PROTO_TYPE>>& protoRes)
{
auto it = m_XMysqls.find(id);
if (it == m_XMysqls.end()) {
return false;
}
return m_XMysqls[id]->exeSqlProtobuf(sql, protoRes);
}
private:
//异步类型 有一些不需要马上得到结果的操作可以在这完成
std::map<uint32_t, std::shared_ptr<XMysql>> m_asyncXMysqls;
public:
std::string m_name = "";
uint32_t m_type = 0;
uint32_t m_id = 0;
};
extern Process* g_process;
#endif
#include "Process.h"
Process* g_process = nullptr;
Process::Process()
{
if (!g_process) {
g_process = this;
}
if (!m_config) {
m_config = std::make_shared<XConfig>();
}
}
Process::~Process()
{
}
bool Process::init(const std::string config)
{
try {
// 加载配置
if (!m_config->load(config)) {
std::cout << "load config error" << std::endl;
return false;
}
// 初始化进程
if (!initProcess()) {
std::cout << "initProcess error!" << std::endl;;
return false;
}
} catch (std::exception& ex) {
std::cout << "Process, init ex= " << ex.what() << std::endl;
}
std::cout << "Process, init success!" << std::endl;
return true;
}
bool Process::start()
{
try {
// 开启服务逻辑
if (!startProcess()) {
std::cout << "startProcess error!" << std::endl;
return false;
}
} catch (std::exception& ex) {
std::cout << "PROCESS, start ex = " << ex.what() << std::endl;
}
std::cout << "PROCESS, start success!" << std::endl;
return true;
}
bool Process::stop()
{
try {
//关闭服务逻辑
stopProcess();
std::cout << "PROCESS, " << m_name << "stopped" << std::endl;;
} catch (std::exception& ex) {
std::cout << "stop ex, " << ex.what() << std::endl;
}
return true;
}
std::string Process::getConfigValue(const std::string groupKey, const std::string key)
{
return m_config->getConfigValue(groupKey, key);
}
bool Process::addMysqlServer(uint32_t id, const char* host, \
const char* port, const char* user, \
const char* pwd, const char* db, \
uint32_t timeout)
{
auto it = m_XMysqls.find(id);
if (it != m_XMysqls.end()) {
return true;
}
auto mysql = std::make_shared<XMysql> ();
mysql->init(host, port, user, pwd, db, timeout);
m_XMysqls[id] = mysql;
return true;
}
bool Process::exeSql(uint32_t id, const char* sql)
{
auto it = m_XMysqls.find(id);
if (it == m_XMysqls.end()) {
return false;
}
return m_XMysqls[id]->exeSql(sql);
}
XMysqlRes Process::exeSqlStore(uint32_t id, const char* sql)
{
auto it = m_XMysqls.find(id);
if (it == m_XMysqls.end()) {
return XMysqlRes();
}
return m_XMysqls[id]->exeSqlStore(sql);
}
④把main.cpp也改一改
/**********************************************************
* Author : 谢名聪
* Email : 869408604@qq.com
* Last modified : 2022-04-21 11:42
* Filename : main.cpp
* Description : 程序的入口
* *******************************************************/
#include "Process.h"
#include <iostream>
#include <signal.h>
#include <sys/stat.h>
void sig_handler(int sig)
{
//接收到信号关闭进程
g_process->stop();
}
void set_signal_handlers(void)
{
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sig_handler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
}
int main(int argc, char** argv)
{
//没有传配置文件
if (argc < 2) {
std::cout << "need config" << std::endl;
return -1;
}
//没有初始化g_process
if (g_process == nullptr) {
std::cout << "g_process == nullptr" << std::endl;
return -1;
}
//加载配置
if (!g_process->init(argv[1])) {
std::cout << "g_process init error" << std::endl;
return -1;
}
//当接收到关闭信号,则关闭程序
set_signal_handlers();
//主进程
if (!g_process->start()) {
std::cout << "process error" << std::endl;
return -1;
}
std::cout << "main.cpp stop" << std::endl;
return 0;
}
⑤写一个XProcess测试一下我们这个框架是否可用
/**********************************************************
* Author : 谢名聪
* Email : 869408604@qq.com
* Last modified : 2022-04-21 11:59
* Filename : XProcess.h
* Description : 进程的子类,需要实现三个纯虚函数
* *******************************************************/
#ifndef X_PROCESS
#define X_PROCESS
#include "../Process.h"
class XProcess : public Process
{
private:
virtual bool initProcess() override;
virtual bool startProcess() override;
virtual bool stopProcess() override;
private:
bool m_stop = false;
};
extern XProcess xprocess;
#endif
#include "XProcess.h"
#include <unistd.h>
#include <time.h>
XProcess xprocess;
bool XProcess::initProcess()
{
//加载数据库
std::string host = getConfigValue("mysql", "host");
std::string port = getConfigValue("mysql", "port");
std::string user = getConfigValue("mysql", "user");
std::string pwd = getConfigValue("mysql", "pwd");
std::string db = getConfigValue("mysql", "db");
std::string sqlType = getConfigValue("mysql", "type");
uint32_t type = std::atoi(sqlType.c_str());
if (!addMysqlServer(type, host.c_str(), port.c_str(), user.c_str(), pwd.c_str(), db.c_str())) {
std::cout << "addMysqlServer error" << std::endl;
return false;
}
//加载服务名字和类型
std::string serverName = getConfigValue("server", "name");
std::string serverType = getConfigValue("server", "type");
std::string serverId = getConfigValue("server", "id");
m_name = serverName;
m_type = std::atoi(serverType.c_str());
m_id = std::atoi(serverId.c_str());
return true;
}
bool XProcess::startProcess()
{
while (m_stop == false) {
//获取系统时间戳
time_t timeReal;
time(&timeReal);
timeReal = timeReal + 8 * 3600;
tm* t = gmtime(&timeReal);
printf("%d-%02d-%02d %02d:%02d:%02d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
sleep(1);
};
return true;
}
bool XProcess::stopProcess()
{
std::cout << "stop " << m_name << std::endl;
m_stop = true;
return true;
}
5.测试一下这个结构是否可用
6.小结
①目前来说,还远远没有达到应用级别,但是基本框架已经非常清晰了。后面我准备把Process基类和main.cpp封装成静态链接库的形式,服务器的开发只在Xprocess上完成。
②现在大多数工具都还没有开始写,后面会陆续的完成mysql,socket,timer,log和redis等工具的封装。
③这个服务器后面肯定还会碰到非常多需要开发和优化的地方,后面希望用博客的形式一点点的记录自己的成长。