前两篇关于即时通讯核心技术《微信为什么不丢消息?》《http如何像tcp一样实时的收消息?》的文章反响不错,今天继续即时通讯系列,聊一聊即时通讯中的“现状”。需求来源“在线状态的一致性”(好友在线状态、群友在线状态)是即时通讯领域难以解决的技术问题。如何准确实时的获取好友和群友的在线状态,是今天要讨论的话题。好友状态一致性问题1:用户uid-A登录时,如何获取他所有好友的在线状态?答:(1)服务器要存储所有用户的在线状态(常存储在高可用缓存集群中)->保证状态可查(2)用户状态实时变化。任何用户登录时,需要将服务器的在线状态设置为在线;任何用户登出时,需要将服务器的状态设置为离线->保证服务器状态存储的一致性和实时状态(3)uid-A登录时,先去数据库拉取他的好友列表,然后去缓存获取所有好友的在线状态->保证登录时获取好友状态的一致性和实时性问题2:当用户uid-A的好友uid-B的状态发生变化时(由登录、注销、隐身等触发),uid-A是如何知道这个事件的?方案一:uid-A轮询服务器获取uid-B(其实是你所有的好友)的状态,比如1分钟一次。缺点:(1)如果uid-B的状态发生变化,不是实时获取到uid-A,可能会有1分钟的延迟。(2)如果uid-B的状态不变,uid-A就会有大量无效的轮询请求,占用服务器资源。方案二:当uid-B的状态发生变化时(登录、注销、隐身等触发),服务端不仅修改缓存中-B的状态中的uid,还将本次状态变化的通知推送给uid-B的在线反向好友(反向好友指的是:加了uid-B为好友的人,不是uid-B的好友,这个要注意细节)优点:(1)实时性缺点:(2)当有大量在线好友数,任何一个用户状态变化都会扩散成N条实时通知,这个N称为“消息风暴扩散系数”。假设一个im系统平均每个用户有200个反向好友,平均有20%的反向好友在线,那么消息风暴扩散系数N=40,也就是说任何一个状态变化都会变成40个推送请求。群友状态一致性问题3:群友状态一致性有什么区别,比好友状态一致性更复杂的是什么?为什么不能使用实时推送?答:理论上,群好友状态也可以通过实时推送实现。以保证实时性。但实际上,群友状态一般是通过拉取获取的,因为群友状态的“消息风暴扩散系数”N太大,实时获取系统往往无法承受。假设每个用户平均加20个群,每个群平均有200个用户,仍然假设20%的用户在线,那么为了保证群友的实时状态,每个用户都必须登录并且改变自己的状态通知发送给20*200*20%=800个群友,N=800,也就是说任何一个状态改变都会变成800个推送请求。XXX系统采用群好友状态推送,就没有这个问题?很有可能是XXX系统的用户量和活跃度不够高。问题四:轮询拉取群友状态也会给服务器带来太大的压力。还有哪些优化方法?答:群友数据量太大。虽然每个用户平均加入了20个群,但实际你每次登录都不会进入每个群。它不是采用轮询拉取的方式,而是采用按需拉取和延迟拉取的方式,实时拉取群友的在线状态实际进群的时间,这样既可以满足用户需求(用户感觉状态是实时的,一致的,但实际是进群后拉的),也可以减轻服务器的压力。这是一种常用的方法。更多关于按需拉取和延时拉取的讨论,可以移步《微信为啥这么省流量》。延伸讨论:系统消息/开屏广告的推送和拉取问题5:系统消息/开屏广告一般是推送还是拉取?答:不考虑APP端推送(APP端推送不需要启动APP也不依赖客户端TCP长连接服务端),个人强烈推荐系统消息/开屏广告使用“拉”方式,因为:这类业务往往对消息的实时性要求不高,如果集中推送,“消息风暴扩散系数”过大,容易造成系统抖动;而pull方式可以平滑这种抖动,当用户登录并均匀发起请求时,如果集中推送,往往不关心用户是否“在线”,往往会造成大量离线垃圾邮件;以及pull方式保证只有在线用户才会收到请求。。。有不同的建议,欢迎评论讨论。摘要和建议状态的实时性和一致性是一个难以解决的技术问题。业务受理不同,数据量不同,在线并发量不同,实现方式也不同。个人建议是:好友状态,如果实时性要求不高的话,可以使用轮询拉取同步群友状态。由于消息风暴的扩散系数太大,可以使用按需拉取和延时拉取同步系统消息/开屏广告等对实时性要求不高的业务。您可以使用pull方法获取消息。“消息风暴扩散系数”是指一条消息发出时,N条消息的扩散系数,这个系数跟业务和数据有关。在一定程度上,它的大小决定了技术是采用push还是pull
