鹅厂开源框架tars之基础组件
一、线程安全队列
typedef TC_ThreadQueue<tagRecvData*, deque<tagRecvData*> > recv_queue; 接收队列
typedef TC_ThreadQueue<tagSendData*, deque<tagSendData*> > send_queue; 发送队列
定义如下:实现比较简单TC_ThreadQueue模板类直接继承了:TC_ThreadLock,从之前文章网络层实现的介绍可以看到这个类比较重要,因为从框架中收到的网络包都会加入到这个缓存队列里面,然后多业务线程ServantHandle会调用waitForRecvQueue从该队列里面取网络数据包,然后调用dispatch调用协议消息对应的处理函数
TC_ThreadQueue继承于TC_ThreadLock用于实现线程锁和wait如下:
如上图调用了:Lock lock(*this);加锁 ,避免网络层接收数据和业务层取同一队列的数据冲突
二、TC_ThreadLock普通线程锁
第一点TC_ThreadQueue继承的TC_ThreadLock的定义如下
typedef TC_Monitor<TC_ThreadMutex, TC_ThreadCond> TC_ThreadLock;
其中TC_ThreadMutex代表线程锁:成员变量mutable pthread_mutex_t _mutex;
TC_ThreadCond代表线程信号条件类、所有锁可以在上面等待信号发生成功变量mutable pthread_cond_t _cond;控制条件变量wait
TC_Monitor:线程锁监控模板类.
* 通常线程锁,都通过该类来使用,而不是直接用TC_ThreadMutex、TC_ThreadRecMutex
由第一点使用方法:Lock lock(*this); 如下图构造时候加锁,析构时候解锁实现lock的出栈自解锁功能
三、TC_Thread线程基类
线程基类,所有自定义线程继承于该类,同时实现run接口即可
成员变量包括: 线程锁TC_ThreadLock _lock;用于启动线程时候加锁,防止多线程启动冲突
TC_ThreadControl类用于线程控制:join sleep yield,控制线程类
四、智能指针
TC_HandleBaseT 智能指针基类. 成员变量:atomic_type _atomic; 用于计数
typedef TC_HandleBaseT<TC_Atomic> TC_HandleBase;其中atomic_type为TC_Atomic类型,TC_Atomic为原子操作类,对int做原子操作
TC_AutoPtr智能指针模板类
例子:typedef TC_AutoPtr<ConnStruct> ConnStructPtr;其中ConnStruct结构体继承于TC_HandleBase
TC_AutoPtr拷贝构造引用计数原子操作加1、析构引用计数原子操作减1,当引用计数减少到0时根据设置的开关是否要进行删除来决定是否触发delete
例子: 这是tar使用异步rpc回调的典型例子,这里回调类使用了智能指针
typedef TC_AutoPtr<SessionCallback> SessionCallbackPtr; 定义回调函数智能指针,其中SessionCallback父类继承于TC_HandleBase
//创建回调类SessionCallbackPtr,并传入初始化参数uin gameid等;
SessionCallbackPtr cb = new SessionCallback(iUin, iGameId, iSeqID, iCmd, sSessionID, theServant, current, cs, this);
getSessionPrx()->async_getSession(cb, iUin, iGameId); //异步调用sessionserver远程接口
接口返回完成,回调SessionCallback::callback_getSession(taf::Int32 ret, const MGComm::SessionValue& retValue)函数
接收sessionserver接口的返回的SessionValue结构
因为SessionCallbackPtr使用了智能指针,所以业务不需要去手动释放前面new出来的SessionCallbackPtr,还是比较方便的
五、mysql操作类:TC_Mysql
TC_Mysql封装好的mysql操作类,非线程安全,对于insert/update可以有更好的函数封装,防止SQL注入
使用方式:
TC_Mysql mysql;
//初始化mysql,init时不链接,请求时自动建立链接;
//数据库可以为空;
//端口默认为3306
mysql.init("10.1.36.39", "pc", "[email protected]", "db_dmqq_system");
通常用:void init(const TC_DBConf& tcDBConf);直接初始化数据库:例如:stDirectMysql.init(_stZoneDirectDBConf);
//获取链接的字符集
cout << mysql.getVariables("character_set_client") << endl;
//获取数据
TC_Mysql::MysqlData data;
data = mysql.queryRecord("select * from t_app_users");
for(size_t i = 0; i < data.size(); i++)
{
//如果不存在ID字段,则抛出异常
cout << data[i]["ID"] << endl;
}
//插入数据,指定数据的类型:数值 或 字符串,对于字符串会自动转义
map<string, pair<TC_Mysql::FT, string> > m;
m["ID"] = make_pair(TC_Mysql::DB_INT, "2334");
m["USERID"] = make_pair(TC_Mysql::DB_STR, "abcttt");
m["APP"] = make_pair(TC_Mysql::DB_STR, "abcapbbp");
m["LASTTIME"] = make_pair(TC_Mysql::DB_INT, "now()");
// mysql.insertRecord("t_user_logs", m);
mysql.replaceRecord("t_user_logs", m);
六、网络组件
6.1 TC_Socket 封装了socket的基本方法
- 提供socket的操作类;
- 支持tcp/udp socket;
支持本地域套接字;
再下一层taf封装了TC_TCPClient和TC_UDPClient两个类用于实际操作tcp和udp应用
使用方式:
例如:tcp客户端
TC_TCPClient stRouterClient;
stRouterClient.init(sIP, iPort, iTimeOut); 这里传入ip和端口然后调用sendRecv进行消息的收发
Int32 ret = stRouterClient.sendRecv(request.c_str(), request.length(), recvBuf, iRecvLen);
注意多线程使用的时候,不能多线程同时send/recv,小心串包
6.2TC_Epoller :
提供网络epoll的操作类
默认是ET模式,当状态发生变化的时候才获得通知
提供add、mod、del、wait等基础操作
使用方式:(见tars之网络层实现CommunicatorEpoll部分)
6.3 TC_ClientSocket 客户端socket相关操作基类:
提供关键成员函数init(const string &sIp, int iPort, int iTimeout); 传入ip 端口 和 超时时间
TC_TCPClient继承于TC_ClientSocket 提供成员函数:
sendRecv(发送到服务器, 从服务器返回不超过iRecvLen的字节)
sendRecvBySep( 发送倒服务器, 并等待服务器直到结尾字符, 包含结尾字符)
例子:
stRouterClient.init(sIP, iPort, iTimeOut);
size_t iRecvLen = sizeof(recvBuf)-1;
Int32 ret = stRouterClient.sendRecv(request.c_str(), request.length(), recvBuf, iRecvLen);
同理还有TC_UDPClient实现UDP客户端
七、日志模块
使用方式:
#define LOG (TafRollLogger::getInstance()->logger())
#define LOG_ROLL_ERROR(context) do { if (IS_LOG_ERROR) { LOG->error()<< "[" << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "]|" << context << endl; } } while (0)
LOG_ROLL_DEBUG("user:" << user->getUin() << ",iLevelLimit:" << iLevelLimit);
调用TafTimeLogger::logger函数的时候,根据文件名字对应不同的TimeLogger 默认为空使用TimeLogger *_pDefaultLogger;
logger类图:
八、 命令解析、配置文件
8.1TC_Config
- 配置文件解析类(兼容wbl模式);
- 支持从string中解析配置文件;
- 支持生成配置文件;
- 解析出错抛出异常;
- 采用[]获取配置,如果无配置则抛出异常;
- 采用get获取配置,不存在则返回空;
读取配置文件是线程安全的,insert域等函数非线程安全
例子:
TC_Config config;
config.parseFile(ServerConfig::BasePath + ServerConfig::ServerName + ".conf");
stTmpGameServerConfig.iGameId = TC_Common::strto<UInt32>(config["/Main/<GameId>"]);
配置文件样例
使用get方法例子:如果读不到该配置,则返回默认值:sDefault
stTmpGameServerConfig.iMaxRegNum = TC_Common::strto<Int32>(config.get("/Main/<MaxRegNum>", "20000000"));
8.2 TC_Option
- 命令解析类;
- 通常用于解析命令行参数;
- 只支持双—的参数形式
分析main的输入参数,支持以下形式的参数:
./main.exe --name=value --param1 param2 param3
TC_Option op;
//解析命令行
op.decode(argc, argv);
//获取成对的参数,即获取 - - 表示的所有参数对
map<string, string> mp = op.getMulti();
//表示非 – 的参数:即 param2, param3
vector<string> d = op.getSingle();
如果value,param有空格或者--,用引号括起来就可以了
九、原子计数类
- 原子计数类;
- 4字节整形数的加/减/都是原子的;
例子:
TC_Atomic g_loginSeqNo;
pc.ServiceSeq = g_loginSeqNo.inc();
pc.SeqNo = g_loginSeqNo.inc();
十、通用仿函数类
TC_Functor 参考loki库的设计
1.仿函数对象调用方式, 即对上述的几种方式都可以在右侧添加一对圆括号,并在括号内部放一组合适的参数来调用,例如a(p1,p2);
2.把整个调用(包括参数)封装一个函数对象, 调用对象建立时就传入了参数,调用的时候不用传入参数,例如A a(p1, p2); a();
简单又好用的封装,具体见下面使用例子自然明白:
10.1 C函数调用
void TestFunction3(const string &s, int i){
cout << "TestFunction3('" << s << "', '" << i << "')" << endl; }
//采用函数指针构造对象
TC_Functor<void, TL::TLMaker<const string&, int>::Result > cmd3(TestFunction3);
string s3("s3");
cmd3(s3, 10);
C函数调用用wrapper封装:
//调用封装,构造的时候传入参数
TC_Functor<void,TL::TLMaker<const string&, int>::Result>::wrapper_type fwrapper3(cmd3, s3, 10);
fwrapper3(); //参数已经在构造的时候传入,调用的时候不用传参数了
说明:
- void : 函数的返回值
- TL::TLMaker<const string&, int>::Result : 代表参数类型
对于调用的封装,注意对于传引用类型,具体的调用时候要保证引用的对象存在
10.2 C++指向类成员函数的调用
struct TestMember
{
void mem3(const string &s, int i)
{
cout << "TestMember::mem3(" << s << "," << i << ") called" << endl;
}
}
TC_Functor<void, TL::TLMaker<const string&, int>::Result > cmd3(&tm, &TestMember::mem3);
cmd3("a", 33);
指向类成员函数的调用用wrapper封装:
TC_Functor<void, TL::TLMaker<const string&, int>::Result >::wrapper_type fwrapper3(cmd3, "a", 10);
fwrapper3();
一十一、强大的loki库
在编译期间对类型的特性进行提取和判断
例子:
bool b;
b = TL::TypeTraits<vector<int>::iterator>::isPointer;
cout << "vector<int>::iterator is " << b << endl;
b = TL::TypeTraits<char*>::isPointer;
cout << "char* is " << b << endl;
b = TL::TypeTraits<char&>::isReference;
cout << "char& is " << b << endl;
11.2.TypeSelect 类型选择器 在编译期从两个类型中选择适当类型
例子:字符串转换模板
template<typename T>
T TC_Common::strto(const string &sStr)
{
typedef typename TL::TypeSelect<TL::TypeTraits<T>::isStdArith, p::strto1<T>, p::strto2<T> >::Result strto_type;
return strto_type()(sStr);
}
模板使用例子:字符串转整形
stTmpGameServerConfig.iGameId = TC_Common::strto<UInt32>(config["/Main/<GameId>"]);
一十二、util/tc_hash_fun.h
*可以对输入的字节流进行hash得到相当均匀的hash值 ;使用例子:
未完待续.....