在线用户列表数据结构设计

来源:互联网 发布:淘宝空包代发靠谱吗 编辑:程序博客网 时间:2024/06/12 01:16

         每次说起自己项目里面涉及到的数据结构时,总是说得稀里糊涂的,完全表达不清楚。今天尝试着写出来,顺便理清楚自己的思路。

        我之前是在做一个基于web的接入认证和控制系统,其中包括了对用户在线时长统计的功能需求。在web认证的流程里,用户经过认证之后,用户的浏览器仍然需要向认证服务器发送心跳请求,通知服务器该用户仍然在线。当在一定的间隔内,没有收到用户的心跳请求时,就认为该用户已经异常掉线。所以为了进行掉线检测,我维护了一个在线用户列表。问题的关键是数据结构应该如何设计,才可以快速找到已经掉线的用户。

        1)HashMap+Array

        由于在线用户的IP地址是唯一的,所以我将IP地址作为key利用HashMap存储在线用户的信息。每个在线用户的信息都应该包括一个心跳请求发送的时间。这样我就可以根据心跳请求发送的时间去判断用户是否已经出现了异常掉线的情况。如果需要找出所有异常掉线的用户,我需要对HashMap进行遍历。而对HashMap的遍历需要较多的构造临时对象的开销,所以我在添加在线用户时,在一个数组里面维护当前用户的一个引用,这样可以避免每次遍历HashMap需要的构造临时对象的开销。同时,HashMap的在线用户对象也保存了对应的数组引用对象的下标信息,建立了一对一的关系。

        在这样的处理下,对在线用户列表的遍历就变成了对引用数组的遍历。对在线用户的增加、修改和删除操作都是可以在O(1)的时间复杂度里面实现,但是遍历的操作仍然需要O(n)的时间。

       2)HashMap+Heap

       如果希望快速寻找到已经掉线的用户,最好的方法就是根据心跳发送请求的时间对在线用户列表进行排序。可是排序的时间复杂度是相当高的,为了降低排序的开销,只对在线用户列表进行部分的排序。利用数组实现最小堆结构,这样当前最早发送心跳请求时间的在线用户都是处于堆顶的,如果堆顶的用户已经掉线,删除堆顶用户元素并调整最小堆,直到找出所有的掉线用户。可是维持这样一个最小堆的结构所需要的开销很大,因为每次用户发送心跳更新请求都需要根据心跳更新时间调整最小堆。

      3)HashMap+List

      由于用户浏览器发送心跳更新请求都是有时间顺序的,所以我再根据心跳更新时间对在线用户列表进行排序根本就是多余的。我只需要维护一个心跳更新请求列表,相当于用户的访问列表,不过需要保持心跳请求发送的有序性。所以我选择的是带头结点的双向循环链表结构。选择双向链表是因为动态插入和删除的性能好,而选择带头结点是为了体现出有序性。从链表的尾部到头部,心跳请求的发送时间是增加的。所以查找已经掉线的用户可以从链表尾部开始查找。

        所以问题的关键是保持链表在时间上的有序性。当用户发送心跳更新请求的时候,需要在访问列表上删除原先的访问信息结点,并在头结点增加新的访问结点。由于结点的信息是基本相同的,所以只需要修改结点的前后指针即可。相应的,HashMap应该保存访问列表上的结点信息。结合HashMap的快速查找以及双向链表的动态修改的特点,可以很好地实现在线用户列表的维护,并且快速地找到异常掉线的用户。