Welcome

首页 / 软件开发 / C++ / Muduo 网络编程示例(八)用 Timing wheel 踢掉空闲连接

Muduo 网络编程示例(八)用 Timing wheel 踢掉空闲连接2014-04-03 csdn 陈硕本文介绍如何使用 timing wheel 来踢掉空闲的连接,一个连接如果若干秒没有收到数据,就认为 是空闲连接。

本文的代码见 http://code.google.com/p/muduo/source/browse/trunk/examples/idleconnection

在严肃的网络程序中,应用层的心跳协议是必不可少的。应该用心跳消息来判断对方进程是否能正 常工作,“踢掉空闲连接”只是一时权宜之计。我这里想顺便讲讲 shared_ptr 和 weak_ptr 的用法。

如果一个连接连续几秒钟(后文以 8s 为例)内没有收到数据,就把它断开,为此有两种简单 粗暴的做法:

每个连接保存“最后收到数据的时间 lastReceiveTime”,然后用一个定时器,每秒钟遍历一遍所 有连接,断开那些 (now - connection.lastReceiveTime) > 8s 的 connection。这种做法全局只 有一个 repeated timer,不过每次 timeout 都要检查全部连接,如果连接数目比较大(几千上万), 这一步可能会比较费时。

每个连接设置一个 one-shot timer,超时定为 8s,在超时的时候就断开本连接。当然,每次收到 数据要去更新 timer。这种做法需要很多个 one-shot timer,会频繁地更新 timers。如果连接数目比 较大,可能对 reactor 的 timer queue 造成压力。

使用 timing wheel 能避免上述两种做法的缺点。timing wheel 可以翻译为“时间轮盘”或“刻度 盘”,本文保留英文。

连接超时不需要精确定时,只要大致 8 秒钟超时断开就行,多一秒少一 秒关系不大。处理连接超时可以用一个简单的数据结构:8 个桶组成的循环队列。第一个桶放下一秒将 要超时的连接,第二个放下 2 秒将要超时的连接。每个连接一收到数据就把自己放到第 8 个桶,然后 在每秒钟的 callback 里把第一个桶里的连接断开,把这个空桶挪到队尾。这样大致可以做到 8 秒钟 没有数据就超时断开连接。更重要的是,每次不用检查全部的 connection,只要检查第一个桶里的 connections,相当于把任务分散了。

Timing wheel 原理

《Hashed and hierarchical timing wheels: efficient data structures for implementing a timer facility》这篇论文详细比 较了实现定时器的各种数据结构,并提出了层次化的 timing wheel 与 hash timing wheel 等新结构 。针对本文要解决的问题的特点,我们不需要实现一个通用的定时器,只用实现 simple timing wheel 即可。

Simple timing wheel 的基本结构是一个循环队列,还有一个指向队尾的指针 (tail), 这个指针每秒钟移动一格,就像钟表上的时针,timing wheel 由此得名。

以下是某一时刻 timing wheel 的状态,格子里的数字是倒计时(与通常的 timing wheel 相反),表示这个格子(桶 子)中的连接的剩余寿命。