tars开源框架地址:https://github.com/Tencent/Tars
系列文章:
简介:Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(Total Application Framework),目前支持C++,Java,PHP,Nodejs语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。目前该框架在腾讯内部,各大核心业务都在使用,颇受欢迎,基于该框架部署运行的服务节点规模达到上万个。
tars开源框架库里面用cpp实现了比较多的公用组件,这些组件一般统一放在util文件夹,在应用层也可以自由使用,工欲善其事必先利其器,所以有必要把这些工具组件做了解,更好的使用,提高效率
一、线程安全队列TC_ThreadQueue
先看下框架对TC_ThreadQueue类的使用如下:
typedef TC_ThreadQueue<tagRecvData*, deque<tagRecvData*> > recv_queue; 接收队列
typedef TC_ThreadQueue<tagSendData*, deque<tagSendData*> > send_queue; 发送队列
TC_ThreadQueue的实现比较简单继承了TC_ThreadLock,从之前文章
《鹅厂开源框架tars之网络层实现》实现的介绍可以看到这个类比较重要,因为从框架中收到的网络包都会加入到这个缓存队列里面,然后多业务线程ServantHandle会调用waitForRecvQueue从该队列里面取网络数据包,然后调用dispatch调用协议消息对应的处理函数,先看下框架对TC_ThreadQueue的实现:
TC_ThreadQueue继承于TC_ThreadLock用于实现线程锁和wait如下(TC_ThreadLock 为普通线程锁,在下面介绍),看下队列的成员函数:push_front在队列前面加入数据,
如上图调用push_front函数的时候调用Lock lock(*this)加锁 ,避免网络层接收数据和业务层取同一队列的数据冲突,notify()通知等待在该锁上某一个线程醒过来 ,调用该函数之前必须加锁, ,因为有数据过来了,例如网络层有线程需要取包并进行分发处理
再看一个成员函数pop_front,从头部获取数据, 没有数据则等待.millsecond 阻塞等待时间(ms)
* 0 表示不阻塞
* -1 永久等待
template<typename T, typename D> bool TC_ThreadQueue<T, D>::pop_front(T& t, size_t millsecond)
{
Lock lock(*this);
if (_queue.empty())
{
if(millsecond == 0)
{
return false;
}
if(millsecond == (size_t)-1)
{
wait();
}
else
{
//超时了
if(!timedWait(millsecond))
{
return false;
}
}
}
if (_queue.empty())
{
return false;
}
t = _queue.front();
_queue.pop_front();
assert(_size > 0);
--_size;
return true;
}
BindAdapter::waitForRecvQueue的函数就是调用了pop_front函数,用于等待接收队列,函数原型如下:
这里BindAdapter::waitForRecvQueue调用waitForRecvQueue用于业务线程在等待服务器监听的适配器收到网络包后进行业务包的处理,这里传入的iWaitTime是0表示不阻塞等待数据,立即返回
二、TC_ThreadLock普通线程锁
第一点TC_ThreadQueue继承的TC_ThreadLock类的定义如下
typedef TC_Monitor<TC_ThreadMutex, TC_ThreadCond> TC_ThreadLock;
TC_Monitor 线程锁监控模板类.通常线程锁,都通过该类来使用,而不是直接用TC_ThreadMutex、TC_ThreadRecMutex 类的定义template <class T, class P> class TC_Monitor 需要传入两个模板参数,TC_Monitor 包括以下成员变量:
T _mutex; //互斥锁
mutable P _cond;//条件变量
typedef TC_LockT<TC_Monitor<T, P> > Lock;
typedef TC_TryLockT<TC_Monitor<T, P> > TryLock;
第一个参数TC_ThreadMutex代表线程锁:同一个线程不可以重复加锁 ,包含成员变量mutable pthread_mutex_t _mutex;互斥锁(延伸阅读,这里TC_ThreadMutex.h还包括另外一个类:TC_ThreadRecMutex: *循环锁(一个线程可以加多次锁),使用和定义场景如下:)
typedef TC_Monitor<TC_ThreadRecMutex, TC_ThreadCond> TC_ThreadRecLock;定义于TC_Monitor.h文件中
第二个参数TC_ThreadCond代表线程信号条件类:所有锁可以在上面等待信号发生成功变量mutable pthread_cond_t _cond;控制条件变量wait.结合实际的使用场景,TC_Monitor::timedWait()会调用TC_ThreadCond对象的timedWait函数,下一步调用posix线程库的pthread_cond_wait;TC_ThreadCond::signal()实现发送信号, 等待在该条件上的一个线程会醒
TC_LockT类定义:template <typename T> class TC_LockT锁模板类其他具体锁配合使用,构造时候加锁,析够的时候解锁.
TC_LockT构造函数,传入互斥量初始化成员变量_mutex,TC_LockT构造函数实现:TC_LockT(const T& mutex) : _mutex(mutex)。分析到这里就可以推导出TC_Monitor 定义的typedef TC_LockT<TC_Monitor<T, P> > Lock;这里Lock类型,这里Lock类型的模板参数用的是TC_Monitor类。然后由第一点的线程安全队列TC_ThreadQueue的实际使用场景如下:
Lock lock(*this);
因为TC_ThreadQueue继承于TC_Monitor类,所以这里等于定义了TC_LockT<TC_Monitor<T, P> > lock栈变量,改造时候调用
TC_LockT的构造函数,传入参数this为TC_Monitor的子类对象,TC_LockT的构造函数调用_mutex.lock();实际就是调用了TC_Monitor对象的lock函数,TC_Monitor的lock函数实现:{_mutex.lock(); _nnotify = 0;} 由上文的分析可知这里_