现有场景与存在的问题

在信息、通知等实时性较强的系统中,一次性触达10W+的用户是比较常见的场景。由于触发的地方比较多,那么10分钟內可能产生100w+的推送量。

微信订阅消息接口限制了单个用户推送,也就是不能批量推送。例如有100w的用户需要推送,那么按常规思路就是循环100w次发送100w次的HTTP请求。哪怕是使用多线程在这个量级几乎也是做不到较强的实时性。

实时性的场景:例如自然灾害(台风、暴雨等)的预警通知等。

方案演进

方案一

1.业务服务统一采用MQ的方式将推送数据通过推送服务存储到MySQL。

2.定时任务会定时拉取推送的数据执行推送,然后再将结果写入到MySQL中。

微信订阅消息在实时场景下百万级批量推送的方案(混合云)_微信

存在的问题

1.JOB任务例如每次拉取1w条数据,执行需要大概10分钟,即便采用多线程拉取执行,性能也不高,导致推送的时效性很低,并且推送消息会随之积累,造成恶性循环。

2.JOB任务在读取任务的同时也会写入推送的结果,所以这里推送消息和推送结果不能仅依赖于一张表,否则读写操作会特别的频繁导致整个数据库崩掉。

3.隐藏的问题,由于微信的Token有效期仅有2小时,那么还需要在推送的保证Token的有效期,否则可能某些任务在推送执行到一半的时候由于Token失效导致后续的任务推送失败。

4.如果JOB任务异常宕机也会导致任务的丢失或者重新拉取任务导致的重复推送。

5.这种设计在日常推送的数量不大的时候仍可以满足,但是随着数据量的增长弊端也逐渐显露。


方案二

先解决微信Token失效的问题:建设Token中控服务,参考微信官方的建议。

微信订阅消息在实时场景下百万级批量推送的方案(混合云)_架构设计_02

搭建消息任务执行的客户端,由服务端操作任务的下发,客户端负责任务的执行和结果集的上报。

微信订阅消息在实时场景下百万级批量推送的方案(混合云)_微信_03


核心的调度由推送服务来决定,源源不断的通过MQ发布到客户端上,客户端整合Redis,实现任务消息的临时存储。用于解决断网、崩溃等异常导致的任务丢失以及重复推送的问题。

Token负责保证每次获取的微信Token都是有效的。

这样的设计在一定程度上也有所改善目前的问题,可以通过Docker快速部署客户端增加处理的效率。

备注:客户端链接的Redis是单独的和Token中控服务不互通,主要考虑混合云方式。


存在的问题

1.推送服务目前仅做了数据的分发,但是数据量的问题仍旧没有解决,由于MQ的策略和分发任务的策略导致性能的瓶颈在客户端上,可能某个客户端还没有消费完任务随即又推送了任务过来,导致任务的挤压在客户端;并且任务的取消还是一件麻烦事。

有可能客户端在不同的网络环境,不能够直接操作(例如混合云架构),可能还需要对客户端做数据的监控。

2.Token中控只能保证它分配的Token是有效的,客户端想要保证严格的实时性就需要推送之前都从Token中控服务发起调用(Http),这样的话Http的调用就会加倍,一是原本微信推送的Http调用,二是增加了Token获取的调用。

3.目前的性能瓶颈主要集中在了客户端消息的积累。


方案三

目前推送主要分为服务端(负责消息的存储和调度)和客户端(负责任务的处理和结果的上传)。

微信订阅消息在实时场景下百万级批量推送的方案(混合云)_架构设计_04

主要优化的几个点:

1.将任务的获取方式由原本服务端通过MQ的方式推送修改为客户端主动调用HTTP接口获取任务,主要解决消息积累的问题,由客户端判断当前是否有能力获取任务执行推送。

2.将Token的中控与Redis结合,定时获取Token刷新本地的缓存,在一定程度上减少了HTTP的请求。

3.客户端不需要依赖MQ服务,只需要依赖HTTP和一个高性能的Redis。

4.Docker可以快速的部署客户端实现临时大量的消息积压,并且客户端不需要都在一个云架构里面,可以存在于阿里云、腾讯云、华为云等,甚至本地局域网内,只需要有网络即可。

5.可以根据消息的量级做一个Docker的弹性扩容,至此基本满足现有业务的运行。