im即时通讯开发:群消息推送如何保证实时性

众所周之,群聊是移动端IM的服务端技术难点所在,难在哪?大量的群聊消息,是一条条推给群内成员还是可以使用什么样的优化策略?试想一个2000人大群,一条消息的发出,如果瞬间被扩散写成2000条一对一消息的投递,对于接收方而言不过是一条消息而已,而服务端是以对相对比单聊消息的2000倍处理压力后的结果。那么服务端在保证消息投递的同时,面对这么大的压力该如何解决好效率问题?解决不好效率问题那实时性就不能保证!

当然,实际在生产环境下,群消息的发送都会想尽办法进行压缩,并开展各种改善性能的处理办法,而不是像上述举例里的直接扩散写(即2000人群里,一条消息被简单地复制为2000条一对一的消息投递)。

先大致分析一下问题产生的原因。

1)消息量瞬间大增:

抢红包时大家都比较活跃,不停在群里发消息,尤其群成员比较多的群(500人),每条消息都会给服务端带来大量的计算工作。

im即时通讯开发:群消息推送如何保证实时性_第1张图片

2)后台逻辑不够优化:

比如红包消息没有单独的通道,时效性会收到其他消息影响、没有采用批处理方式、异步处理有些环节还不到位等等。即时通讯聊天软件开发可以咨询蔚可云。

精确定位问题的原因

我们尝试精确定位问题的根本原因,原因分析如下。

1)c2g模块没有采取批处理方式:

1条群(500人群)消息到达c2g模块后,c2g模块为每个人写收件箱(这里时间延迟较大,优化点),然后在把这条消息变成500条投递消息(需要批处理,就给Kafka放入一条消息),通过Kafka送给Deliver节点投递。

2)Deliver模块的处理没有批量合并:

Deliver模块会到Redis中逐条(500条)检索接收消息用户的在线状态(这个点需要批处理,根据用户Id分布,一次检索若干用户的在线状态),在线的投递消息(批处理),离线的发送第三方push(批处理)。

3)离线推送流程不优化:

整体流程上,每条消息是先写了离线收件箱,再推送。这样效率也不高,需要对这个流程细化以及异步化。

总结一下就是:

微信在这块的一个重要优化思想是批处理,做法是单次批量操作(我们本次优化目标)裸写,多条消息的聚合(MapReduce过程)下沉到了MQ中间件中。

群聊红包逻辑单独部署

现阶段,当消息(尤其是大群消息)量大的时候,Deliver节点会成为瓶颈。红包对时效性要求很高,架构上采用独立为红包部署Deliver节点的方式确保红包消息走单独通道进行推送。即使其他消息出现延迟,红包消息依然能保证即使送达。

裸写批处理逻辑

处理一条群消息,服务端要进行大量的工作,需要查询所有群成员的路由表、在线状态,在线人员需要推送及时消息,离线人员需要推送第三方push(比如iOS的apns推送通道)。这些工作逐条执行,性能会非常差,如果遇到大群,系统会不可用。

批处理可以较好解决这个问题。比如用户状态及路由表数据,采用hash算法分布在几台服务器上。收到群消息后,根据群成员,计算出用户状态及路由表数据的分布情况,从缓存服务器中一次检索出该服务器可能存在的所有群成员状态及路由信息。这样可以极大减少RPC调用次数,及计算量。

推送操作也类似,批量向接入层投递消息即可。

离线消息异步写收件箱

im即时通讯开发:群消息推送如何保证实时性_第2张图片

在处理大群消息推送时,写离线消息也是一个非常影响性能的地方。现有的逻辑是先为每个人写一条离线消息,再执行推送。

1)Deliver节点收到一条群消息,检索用户在线状态及路由信息,用户在线(离线的逻辑相对简单,略过);

2)批量推送消息(2、批处理逻辑);

3)异步将消息写入消息总线,同时写入第三方push的延迟推送任务;

4)异步写离线消息(不影响在线用户收到消息的速度);

5)第(2)步推送消息的ack信息回到服务端;

6)c2g模块将ack信息放入消息总线。(确保消息时序性,ack需要在写离线消息之后处理,否则可能出现消息重复);

7)删除对应的离线消息;

8)第(3)步写入的延迟推送任务,在规定时间(如10秒)后生效,判断是否存在此条离线消息(如果ack回来了,离线消息会被删掉),如果离线消息还存在,发送第三方push。

通过以上3个方面的优化,能够确保在并发消息量较大时,推送消息依然及时。

你可能感兴趣的:(即时通讯)