鹅厂开源框架tars之基础组件

一、线程安全队列

    typedef TC_ThreadQueue<tagRecvData*, deque<tagRecvData*> > recv_queue;  接收队列
    typedef TC_ThreadQueue<tagSendData*, deque<tagSendData*> > send_queue; 发送队列

   定义如下:实现比较简单TC_ThreadQueue模板类直接继承了:TC_ThreadLock,从之前文章网络层实现的介绍可以看到这个类比较重要,因为从框架中收到的网络包都会加入到这个缓存队列里面,然后多业务线程ServantHandle会调用waitForRecvQueue从该队列里面取网络数据包,然后调用dispatch调用协议消息对应的处理函数

   

鹅厂开源框架tars之基础组件

TC_ThreadQueue继承于TC_ThreadLock用于实现线程锁和wait如下:

鹅厂开源框架tars之基础组件

如上图调用了: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的出栈自解锁功能

鹅厂开源框架tars之基础组件

 

三、TC_Thread线程基类

 线程基类,所有自定义线程继承于该类,同时实现run接口即可

成员变量包括: 线程锁TC_ThreadLock   _lock;用于启动线程时候加锁,防止多线程启动冲突

 

鹅厂开源框架tars之基础组件

TC_ThreadControl类用于线程控制:join sleep  yield,控制线程类

鹅厂开源框架tars之基础组件

 

四、智能指针

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;

       //初始化mysqlinit时不链接,请求时自动建立链接;

       //数据库可以为空;

       //端口默认为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的基本方法 

  1. 提供socket的操作类;
  2. 支持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类图:

鹅厂开源框架tars之基础组件

 

八、 命令解析、配置文件

  8.1TC_Config

  1. 配置文件解析类(兼容wbl模式);
  2. 支持从string中解析配置文件;
  3. 支持生成配置文件
  4. 解析出错抛出异常;
  5. 采用[]获取配置,如果无配置则抛出异常;
  6. 采用get获取配置,不存在则返回空;

读取配置文件是线程安全的,insert域等函数非线程安全

   例子:

    TC_Config config;
    config.parseFile(ServerConfig::BasePath + ServerConfig::ServerName + ".conf");
    stTmpGameServerConfig.iGameId = TC_Common::strto<UInt32>(config["/Main/<GameId>"]);

    配置文件样例

鹅厂开源框架tars之基础组件

  使用get方法例子:如果读不到该配置,则返回默认值:sDefault

  stTmpGameServerConfig.iMaxRegNum = TC_Common::strto<Int32>(config.get("/Main/<MaxRegNum>", "20000000"));

8.2 TC_Option 

  1. 命令解析类;
  2. 通常用于解析命令行参数;
  3. 只支持双—的参数形式

分析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有空格或者--,用引号括起来就可以了

 

九、原子计数类

    TC_Atomic

  1. 原子计数类; 
  2. 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();  //参数已经在构造的时候传入,调用的时候不用传参数了

说明:

  1. void : 函数的返回值
  2. 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库

11.1 TypeTraits

在编译期间对类型的特性进行提取和判断

例子:

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值 ;使用例子:

鹅厂开源框架tars之基础组件

未完待续.....