如何实现 开心网/校内网 在线状态的显示

开心网, 校内网 的个人首页的我的好友列表中 在线的好友 都会有一个特殊的小图标,如何高效地实现这个功能呢?

 

先来讨论,如何定义该用户是登录的呢?

第一, 启用了session, session有效,则认为是登录的。

第二, 通过Cookie记录的,Cookie有效的就认为是登录的,也可以设置session cookie。

 

这样,获取登录状态就好办了,那么如何获取好友的登录状态呢,cookie/session都只能确定自己的是登录的,这就需要在初始化session或者cookie的时候找个地方把这个状态记录下来,在用户有动作时,更新这个字段,可能的记录方式:

第一,  直接和个人数据一起保存在数据库中(优缺点,下面讨论)

第二,独立保存用户的登录状态(可能的方案,下面讨论)

第三,将自己的状态分发给所有的好友,每个用户都有一个字段保存自己好友的登录状态(写是个问题,待会儿讨论)

 

状态保存下来了,那么怎么确定用户已经离开了呢? 我了解的方案是,约定一个时间,比如10分钟,如果用户在10分钟还没有动作,则认为该用户已经离开,这样,仅仅保存一个状态就不行了,需要保存的应该是最近访问的时间戳。

 

现在我们再来讨论如何读取这些数据。

方案一, 按照第一种方式储存的,和好友数据一起读取出来,如果按照校内的实现方式,这个是没有问题了,校内的在线的好友并没有前置;而开心是将登录的好友前置了的,这样就有需要一个orderby,将在线的用户优先选出来,事实上开心不是这样做得。

方案二,  按照第二种方案储存,由于数据独立保存, 所以,按照校内的方式,直接遍历前面几个好友的状态就好了;而按照开心的显示方式则需要遍历所有的数据, 获取其状态,然后前置

方案三, 按照第三种方案储存,这个就比较简单了,直接读取就好了。

 

来看看这些解决方案的优缺点:

一,优点,简单,易实现。缺点,每次用户访问都需要更新用户的个人数据表,无疑给数据库带来了很大的压力,变通的方式,可以考虑如果上次访问时间和记录的时间间隔小于约定的一个值(比如5分钟),不更新,在一定程度上可以减少写操作。更好的方式是将这个字段从个人数据表中分离出来,独立保存在一个表中,事实上就是第二中方案的一种,这个可以使用join来和原表一起查询获取用户的状态,但是在这儿,我却要说,强烈建议你不要使用 join, 如果访问量少的话, 没有问题,但是当查询量很大的时候问题就很明显 了,会严重降低数据库的响应时间。

二,独立保存用户状态的方案,如上所述,保存在一张独立的表中,甚至是 heap表(如果你使用的是MySQL的话), 或者保存在 memcached / memcachedb, tokyocabinet 中, 再或者其他系统中,如果是独立的表,可能采用join方式,但是强烈反对这么做,除非你用户量很少,建议的方案是 首先获取所有的好友id, 然后使用 select user_id from user_id in ([friend_id list ]), 数据都保存在内存中的话,效率不会很差的,今天测试了一下,如果有1000w数据,好友有1k,使用innodb,单个进程在 10ms左右。 如果保存在 memcached或者其他非关系型数据库就需要遍历所有的好友了,这个方案其实也是我认为最好的方案,但是至于使用什么系统保存,则当别论。另外,保存在内存中的话,数据可能会丢,那就要看产品对于这个功能的要求了,个人觉得,无所谓了。

三,每个用户保存一份好友的状态数据,一般不会很多,但要考虑极端情况,这儿需要使用到 异步处理,因为你不可能实时去更新好友的数据,那样用户体验会很差的。对于开源的异步中间件,了解的不是很多,有没有人了解呢,帮忙介绍下? 这种方案就是空间换时间了,写操作也会多一些,比如平均1个用户有10个好友,那么写的数据将是其他方式的10倍。

 

最后,讨论使用 memcached 或者其他key/value 非关系型数据库的方案,个人觉的,可以通过稍微修改memcached 使其只支持定长的key/value数据,这样效率会更好一些,写的时候可以采用memcached提供的UDP的方式去写,因为我们不关心其返回值,读取的时候可以一次读取多个值,可以是所有的好友的数据,这个我现在还没有测试过,我想结果应该在ms级的,有没有测试过?

 

讨论的应该是对于大型网站比较适用的,如果小网站,那就不需要这么复杂了,还有后面的一些问题也是基于 开心的实现讨论的。

 

This article is posted by on , link is .

Leave a reply

  • says:
    前提条件是数据都放在内存中,否则,性能就不能保证了,一个用户按照32个字节计算,100M用户也只需要 3.2G的内存,至于网络的问题, 网络交互都在内网,每秒按照1000的响应计算 每个请求按照 1k个字节计算 1k * 1k = 1M 这个应该不是问题。
  • says:
    如果一台服务上,磁盘、网络的IO如何保证呢?
  • says:
    按照wxz的说法,ejabberd的代价太高了。 因为这个数据不需要很精确,需要的是高效地大量地读取。 采用memcached 测试了一下, 1000w 的数据 100 并发,每次读取 1000个 key, 可以达到800qps/s。 数据结构为 key:11位, value: int, 启动memcahed时, 每个分配 32个字节。 占用了内存 1G.
  • says:
    我觉得借鉴IM相似的技术就可以吧。呵呵。在线好友。排序。等等。应该没有差别。用ejabberd做msg broadcasting也ok吧?哈哈。