一个简单基于LRU连接检测实现
在做网络应用的时候经常要处理不处于活动的连接,对于不活动的TCP连接可以通过设置KeepAlive来触发SocketError来处理掉.但有更多的时候是使用ping和pong来处理.对于ping,pong这种做法的发起者有两种情况,分别是由server或client发起.对于由服务器发起比较损耗资源毕竟每隔一段时间都要向整个连接列发送ping,当累计到定数量没得到pong回应用的时候杀死;而对于Client发起的话server只需要记录ping时间即可,隔一段时间没有得到ping的client杀死.但两种做法似乎都要对连接列表进行一次扫描,存在大量活动连接的时候这种做法似乎并不理想.
其实可以通过一个LRU算法简单地把活动连接前置,那在处理的时候只需要关心不活动的连接即可以,LRU算法大概如下:
LRU(least recently used)。
假设 序列为 4 3 4 2 3 1 4 2
物理块有3个 则
首轮 4调入内存 4
次轮 3调入内存 3 4
之后 4调入内存 4 3
之后 2调入内存 2 4 3
之后 3调入内存 3 2 4
之后 1调入内存 1 3 2(因为最近最久未使用的是4,所以丢弃4)
之后 4调入内存 4 1 3(原理同上)
最后 2调入内存 2 4 1
可以通过一个简单的链接来完成上面功能,当某个项目接收到ping的时候,移到链表头即可以.在扫链表的时候只需要由尾部扫起,值到某个值不存在超时的时候就可以停止扫描.
通过LRU就能做到在活跃连接比较多的情况有比较好的处理效果,由于链表在找值的时候必须要扫一次,所以当连接更新在链表中寻找相应的值成本是不太划算的.可以通过制订一个双向引用的结构来解决寻找问题.
/// <summary> /// 连接描述接口 /// </summary> public interface IConnecton { /// <summary> /// 获取对应在LRU算法中的节点 /// </summary> LinkedListNode<LRUDetect.Node> Node { get; set; } /// <summary> /// 超时操作,当LRU算法检测到应该连接超时的时候会调用该方法 /// </summary> void TimeOut(); } /// <summary> /// 节点信息 /// </summary> public class Node { /// <summary> /// 最后活动时间 /// </summary> public int LastActiveTime; /// <summary> /// 相关连接信息 /// </summary> public IConnecton Connection; /// <summary> /// 检测是否过期 /// </summary> /// <param name="cutime"></param> /// <param name="timeout"></param> /// <returns></returns> public bool Detect(int cutime, int timeout) { return Math.Abs(cutime - LastActiveTime) > timeout; } }
制定一个应用接口和相应的节点类型.通过这个接口规范就可以方便定位到链表的节点上.
public void Update(IConnecton connection) { lock (this) { LinkedListNode<LRUDetect.Node> node = connection.Node; if (node != null) { node.Value.LastActiveTime = Environment.TickCount; mLinkedList.Remove(node); mLinkedList.AddFirst(node); } else { node = mLinkedList.AddFirst(new Node()); node.Value.LastActiveTime = Environment.TickCount; node.Value.Connection = connection; connection.Node = node; } } }
当一个项更新的时候就不再需要遍历链表查询,这样就能节省大量查询上的损耗.当然一个完整的LRU检测机制不是要一个timer来驱动,完全代码如下:
/// <summary> /// 基于LRU算法的连接检测 /// </summary> public class LRUDetect : IDisposable { /// <summary> /// 构建检测器 /// </summary> /// <param name="timeout">超时时间以毫秒为单位</param> public LRUDetect(int timeout) { mTimeout = timeout; mTimer = new System.Threading.Timer(OnDetect, null, mTimeout, mTimeout); } private int mTimeout; private System.Threading.Timer mTimer; private LinkedList<Node> mLinkedList = new LinkedList<Node>(); /// <summary> /// 更新连接 /// </summary> /// <param name="connection">连接信息</param> public void Update(IConnecton connection) { lock (this) { LinkedListNode<LRUDetect.Node> node = connection.Node; if (node != null) { node.Value.LastActiveTime = Environment.TickCount; mLinkedList.Remove(node); mLinkedList.AddFirst(node); } else { node = mLinkedList.AddFirst(new Node()); node.Value.LastActiveTime = Environment.TickCount; node.Value.Connection = connection; connection.Node = node; } } } /// <summary> /// 删除连接 /// </summary> /// <param name="connection">连接信息</param> public void Delete(IConnecton connection) { lock (this) { LinkedListNode<LRUDetect.Node> node = connection.Node; if (node != null) { node.Value.Connection = null; mLinkedList.Remove(node); } } } private void OnDetect(object state) { lock (this) { int cutime = Environment.TickCount; LinkedListNode<Node> last = mLinkedList.Last; while (last != null && last.Value.Detect(cutime, mTimeout)) { last.Value.Connection.TimeOut(); last.Value.Connection = null; mLinkedList.RemoveLast(); last = mLinkedList.Last; } } } /// <summary> /// 连接描述接口 /// </summary> public interface IConnecton { /// <summary> /// 获取对应在LRU算法中的节点 /// </summary> LinkedListNode<LRUDetect.Node> Node { get; set; } /// <summary> /// 超时操作,当LRU算法检测到应该连接超时的时候会调用该方法 /// </summary> void TimeOut(); } /// <summary> /// 节点信息 /// </summary> public class Node { /// <summary> /// 最后活动时间 /// </summary> public int LastActiveTime; /// <summary> /// 相关连接信息 /// </summary> public IConnecton Connection; /// <summary> /// 检测是否过期 /// </summary> /// <param name="cutime"></param> /// <param name="timeout"></param> /// <returns></returns> public bool Detect(int cutime, int timeout) { return Math.Abs(cutime - LastActiveTime) > timeout; } } /// <summary> /// 释放对象 /// </summary> public void Dispose() { if (mTimer != null) { mTimer.Dispose(); mLinkedList.Clear(); } } }
访问Beetlex的Github