前言
这里的链接管理主要关注下面几个问题:
- 链接_uid生成规则
- 链接刷新和超时踢掉机制
- 链接关闭
一.ConnectionList中的_uid
1.链接管理相关的类图:
- 每个网络线程都有一个独立的链接列表ConnectionList,负责管理该网络线程的所有Connection,ConnectionList继承于TC_ThreadLock,是线程安全的
- ConnectionList的_total表示管理的最大链接数;_free里面存储的是还未使用的唯一id(用于生成链接的标识符_uid);魔数_ConnectionMagic也是用于生成链接的_uid;_tl和_vConn两个成员用于管理所有的链接
- Connection负责socket的接收和发送。除了_socke,_ip,_recvbuffer等与接收和发送相关的成员外,每个Connection还有一个4字节大小的链接标识_uid
2._uid生成规则
每个_uid由两部分生成:魔数+唯一id:
- 魔数的组成:高6位( 上图的x部分)来自当前时间截;网络线程索引的范围是1-15,即只有低4位为有效位,经过左移22位后作为魔数接下来的4位(图中的y部分);最后的22位都为0。因为加入了线程索引的因素,所以每个线程的魔数都不一样。实际上也可以反过来推断为什么tars限制网络线程数最多为15,因为4位二进制最大即为15。
- 唯一id的范围:最小为1,最大为_total。这个_total为所有Adapter允许的最大连接数之和,tars把这个_total最大限制为 (1 << 22)-1了。所以唯一id的高10位全为0,低22位为有效位(上图的z部分)。
- 一个server的_uid个数最多为_total * 网络线程数。如果不考虑文件系统fd的限制,不考虑内存,cpu等,那么这个值就是一个tars服务理论上最大的链接数。
魔数和唯一id的生成代码在ConnectionList::init(uint32_t size,uint32_t iIndex)中:
void TC_EpollServer::NetThread::ConnectionList::init(uint32_t size, uint32_t iIndex)
{
_lastTimeoutTime = TNOW;
_total = size;
_free_size = 0;
//初始化链接链表
if(_vConn) delete[] _vConn;
//分配total+1个空间(多分配一个空间, 第一个空间其实无效)
_vConn = new list_data[_total+1];
_iConnectionMagic = ((((uint32_t)_lastTimeoutTime) << 26) & (0xFFFFFFFF << 26)) + ((iIndex << 22) & (0xFFFFFFFF << 22));//((uint32_t)_lastTimeoutTime) << 20;
//free从1开始分配, 这个值为uid, 0保留为管道用, epollwait根据0判断是否是管道消息
for(uint32_t i = 1; i <= _total; i++)
{
_vConn[i].first = NULL;
_free.push_back(i);
++_free_size;
}
}
- 在init()中,生成了固定的魔数_iConnectionMagic后,把所有的唯一id按1到_total的顺序push到_free中。这个_free用来存储还没被使用的唯一id
- 调用这个init()的地方在NetThread::createEpoll(uint32_t iIndex),在这里可以看到对_total的限制:
void TC_EpollServer::NetThread::createEpoll(uint32_t iIndex) { 省略部分代码 .......... if(maxAllConn >= (1 << 22)) { error("createEpoll connection num: " + TC_Common::tostr(maxAllConn) + " >= " + TC_Common::tostr(1 << 22)); maxAllConn = (1 << 22) - 1; } //初始化连接管理链表 _list.init(maxAllConn, iIndex); }