双十二,个推如何保障高优先级消息实时下发
- 消息推送
- 移动开发
引言
明天就是双十二了,面对井喷的推送需求、百亿级推送下发压力,服务了数十万App的个推,是如何持续优化技术方案,保障高优先级消息实时下发的?本文将从业务场景、方案设计思路、方案阐述等方面讲述个推百亿级消息推送的优先级解决方案。
业务场景
在日常消息推送过程中,个推往往需要应对来自以下四方面的挑战。
• 高并发:单台服务器在每日高峰时段需要响应百万级并发量请求
• 低延迟:需毫秒级响应推送请求,保障亿级消息秒级送达
• 海量数据:需在每日百亿级推送下发请求中,实现消息的实时下发
• 海量用户:需要在个推业务层中对外标识的百亿用户里快速筛选出符合推送条件的目标群体进行消息下发
当同时有多个App进行消息下发时,难免会出现资源竞争的情况, 因此就产生了优先级队列的需求:在下发资源固定的情况下, 高优先级的用户需要有更多的下发资源。
方案设计思路
为了应对上述四点挑战,我们采用了以下三项原则予以指导:
• 保证相对公平、避免饥饿问题
• 避免互相干扰、阻塞
• 动态调整下发速度
业务方案
上文介绍了打包构建方案的流程,但是在实践中,我们会发现随着构建的任务越来越多,构建的环境会变得越来越繁杂,难以管理。
基于以上三点原则,结合具体业务场景,我们制定了如下两套解决方案。
方案概述
• 个推采用基于Kafka 的优先级队列方案,解决了消息下发场景中面临的任务大小不一、下发量大、时延低三大难题。
• 个推采用基于Pulsar 的优先级队列方案,解决了消息回执场景中面临的回执响应时间不一、优先级需实时调整这两大难题。
1、基于Kafka 的优先级队列方案
一般情况下,早上7 - 9点、中午12 - 13点、 晚上19 - 21点这三个时间段,个推收到的推送任务比较集中,其消息量占全天的七八成。在这些推送任务中,我们都会针对手机类型、区域分布、用户群体特征等复杂条件筛选出需要送达的用户,而不同客户的用户量差距较大,范围在几百到上亿之间。面对这些客户同时发送的推送请求时,个推需要保障推送消息实时到达,避免客户之间下发速度互相影响。
业务逻辑
针对实际遇到的挑战与我们的方案规划思路,最终设计的落地方案如图所示:
当消息推送处于低峰期、机器资源处于低水位运行时,消息不进入队列,直接发送给App;
当消息推送并发量变高、机器资源处于中水位运行时,消息进入内部队列排队,待消息被消费后再发送给App;
当消息推送并发量继续增大、机器资源处于高水位运行时,消息需要削峰填谷、降低资源竞争。它的具体实现方式是:
消息进入外部队列Kafka中排队,内部队列只接收从外部队列出来的消息,消息从内部队列被消费后发送给App;
当外部队列内的消息消费完,机器资源运行水位缓慢降低到中水位时,内部队列才开始接收其他渠道的推送消息。这样可以避免消息之间的资源竞争,还可以保持后续处理逻辑的统一性。
下文我们将以资源进入高水位运行时的场景来举例说明。
细节方案
由上图可知,推送消息会根据所属的App ID区分高中低优先级。在同优先级的情况下,Kafka生产者首先会给大中小任务发送不同的topic,由对应的Kafka消费者线程放入有界阻塞队列中;接着按照 6:3:1的比例消费,比如一次取1000条推送消息,高中低优先级将分别获得600、300、100的推送消息量;
然后,调度线程将这批消息下发给手机;如果此时高优先级推送消息处于低峰期,配额没用完,假设只用了300,那么剩下的300配额会按 3:1 的比例分配给中、低优先级的消费者线程,以此充分提高资源的利用率,且达到大中小任务不相互阻塞、不同优先级任务不相互干扰的目的。
2、基于Pulsar 的优先级队列方案
当消息下发后,大多数客户都希望及时了解这条推送消息是否已经到达App端,是否已经被系统展示以及是否被用户及时点击查看,故我们每天发给客户的回执量也是巨大的。此业务场景主要涉及到的挑战有:
由于每个客户的网络情况、机器性能、业务处理逻辑等各不相同,因此个推发给客户的回执响应时间也不同,快的十几毫秒就可返回,慢的则需十几秒甚至更久,且有些客户的响应时间是在这个区间内不定期波动。
由于回执消息数量巨大,所以Pulsar也需要先进入队列排队再发送给客户服务端。有些客户为了提高回执发送速度,会申请调整优先级,我们服务端需要对此进行实时调整,提高回执速度。
结合挑战和上述提及的方案设计思路,我们的落地方案如图所示:
相比基于Kafka 的优先级队列方案,此方案的业务逻辑相对比较简单,所有的回执消息先进入外部队列中,再通过调度线程统一把回执消息发送给客户服务端。
由于我们需要实时调整优先级,且希望回执响应速度不同的客户之间不互相阻塞和影响,所以我们优先考虑了Pulsar组件。该组件的数据传输性能表现优秀且具备创建百万级topic的能力。
个推利用此特性为每个App都创建了不同的topic,为我们后续实时调整优先级和下发速度打下了强有力的基础,详情见细节方案。
细节方案
由上图可知,回执消息会被划分到不同组,组内则会按优先级程度发送给客户,具体步骤为:
首先,手机回执消息到达个推服务端后,消息会进入外部队列Pulsar中排队,接着不同的Pulsar消费者组会依据组内不同的优先级,按照6:3:1的比例获取一批回执消息,然后发送至客户的服务端。
App维度的回执消息所属的组是不固定的,每过一段时间,个推会根据不同的回执响应时间,通过K-means聚类算法动态调整App所属的组别,保证组内的发送速度相近,使回执响应速度不同的客户互不影响,且同组内高优先级用户能拥有较多的回执资源。
总结
以上是个推结合消息下发和回执场景整理出的两种解决方案。概括起来,重点为:
• 场景不同,方案不同
消息下发 -- 高峰期内消息下发量大、推送任务大小不一,消息需要被快速下发,为此个推选择了基于Kafka(内外部)优先级队列方案;
消息回执 -- 需要动态聚合回执响应时间不一的消息、动态调整优先级,为此个推选择了基于Pulsar(不同组别)优先级队列方案。
• 方案不同,MQ不同
内外部优先级队列方案 - - 需要满足短时间内传输大量消息数据,为此个推选择了Kafka组件;
不同组别优先级队列方案 -- 需要为每个App都创建各自的topic,为此个推选择了Pulsar组件。
建议开发者在选择方案的时候,多结合业务场景进行思考,从而找到最为合适的落地方案。服务了数十万App的个推,还会在“消息推送”这个领域深入拓展,持续为开发者们分享前沿的理念与最新的实践方案。
-
每日互动官方微信号
公司动态、品牌活动
-
个推官方微信号
新品发布、官方资讯
-
个推技术实践
技术干货、前沿科技