当前位置: 首页 > 科技观察

我如何使用Redis进行实时订阅推送

时间:2023-03-15 19:03:53 科技观察

前段时间,我开发了公司优惠券收集中心的项目,该项目就是以redis为核心技术实现的。先说凭证代收中心项目。本项目类似于京东APP的优惠券领取中心。当然图是京东截的,公司的没有截。..其中一项功能称为订阅推送,用于接收优惠券。优惠券的订阅推送是什么?即用户订阅了优惠券的推送,提醒信息会在领取优惠券前一分钟推送到用户的APP。本来这个订阅功能应该是消息中心做的,但是他们说短时间内做不出来。所以让负责优惠券的我来做-.-!。具体解决方案是,当到达特定的推送时间点时,优惠券系统调用消息中心的推送接口推送信息。下面我们来分析一下这个功能的业务场景。公司目前有6000万+注册用户,别问是哪一家。..比如你用无门槛优惠券下单立减20元,那么就会有更多人抢这张优惠券。我们保守估计10万+,百万级别的很难说。我们一开始定了20万人,那么这20万条推送信息一分钟就推送完!一个用户可以订阅多张优惠券。所以我们知道这个订阅功能有两个突出的难点:推送的有效性:如果推送慢了,用户会抱怨没有及时通知,错过了开始抢的机会。推送量大:人人都想抢热销券!但是,推送的大小会影响推送的效果。真让人头疼!那么让我们一个一个解决问题吧!推送效果问题:当用户在领券中心订阅领券提醒时,后台会生成一条用户订阅提醒记录,记录推送信息发送给用户的时间点.那么问题就变成了系统如何快速实时选择推送哪些记录!场景一:MQ延迟交付。MQ虽然支持消息延时投递,但是规模太大了,1s5s10s30s1m,不能用于精准的时间投递!而如果用户在执行订阅后取消订阅,删除发送的MQ消息这个操作就有点大了,短时间内很难实现!而用户可以取消再订阅,这就涉及到去重的问题。所以拒绝了MQ方案。方案二:传统的定时任务。这个比较简单。定时任务是加载db中用户的订阅提醒记录,选择当前可以推送的记录。但是有句话说,任何脱离实际业务的设计都是耍流氓~。接下来我们就来分析一下传统的定时任务是否适合我们的业务!能否支持多台机器同时运行?一般不会,只能单机同时运行。存储数据源通常是mysql或者其他传统数据库,存储在单表中。频率支持秒、分、时、日,一般不能太快。由上可知,一般的传统定时任务存在以下缺点:性能瓶颈。只有一台机器在处理,面对海量数据无能为力!效果不佳。定时任务的频率不能太高,太高会给业务数据库造成很大的压力!单点故障。万一跑步机挂了,整个业务就不能用了——。——这是一件可怕的事情!所以传统的定时任务不适合这个业务。..那么我们是无助的吗?其实并不是!我们只需要对传统的定时任务做一个简单的改造就可以了!可以变成一个可以同时在多台机器上运行的定时任务集群,效率可以精确到秒级,拒绝单点故障!其中,需要用到我们强大的redis。解决方案三:定时任务集群首先,我们需要定义定时任务集群要解决的三个问题!1.高效。2、吞吐量大。3、服务要稳定,没有单点故障。下面是整个定时任务集群的架构图。架构很简单:我们将用户的订阅推送记录存储在redis集群的sortedSet队列中,并以提醒时间戳作为分值,然后在我们每个业务服务器中设置一个定时器。频率为二级。我的设置为1s,经过负载均衡后,从某个队列中获取待推送的用户记录并推送。下面我们分析一下结构。1、性能:排除带宽等其他因素,基本与机器数量呈线性关系。机器数量越大,吞吐量越大,机器数量越少,相对吞吐量越低。2、效果:已提升至二级,效果尚可。3、单点故障?不存在的!除非redis集群或者所有服务器宕机。...这里分析一下为什么要用redis?首先,redis可以作为高性能的存储db,性能比mysql好很多,支持持久化,稳定性好。第二个redisSortedSet队列天然支持基于时间的排序,完美的满足了我们选择要推送的记录。ok~既然计划已经有了,那么如何在一天之内实施呢?是的,我设计这个方案是为了完成基本的编码,时间是一天。..因为时间太快了。首先,我们以user_id为key,然后将mod队列的个数hash到redis的SortedSet队列中。为什么会这样呢,因为如果用户同时订阅了两张优惠券,而且推送的时间很接近,这样的两次推送可以合并为一个~,hash比较均匀。以下是部分代码截图:然后我们需要确定队列的个数。通常,我们定义与处理服务器一样多的队列。因为队列太小会造成队列竞争,太多可能导致记录不能及时处理。但是,最佳实践是队列数量应该是动态可配置的,因为在线的集群机器数量会经常变化。大促的时候会不会加机器,业务量会增加,机器数量也会增加,对吧~。于是借用了淘宝的钻石动态配置队列数。我们每次从队列中取多少条记录也是可以动态配置的,这样可以根据实际生产情况随时调整整个集群的吞吐量~。所以我们的定时任务集群还是有一个特性是支持动态调整的~。最后一个关键组件是负载平衡。这个非常重要!因为这个做的不好,可能会导致多台机器同时竞争处理一个队列,影响整个集群的效率!在时间紧迫的情况下,我采用了一个简单实用的算法,利用redis增加key,然后mod队列数。这样,很大程度上保证了不会有两台机器同时竞争一个队列~。最后我们计算出整个集群的吞吐量10(机器数量)*2000(一次拉取数量)=20000。然后将消息以MQ的形式推送到消息中心。发送MQ是异步的,统计其他处理需要0.5s。其实发送一个20W的push就是10秒的事情。ok~至此,我们整个定时任务集群就差不多落地了。如果你问我以后还有什么可以改进的,那就是:加监控,集群怎么没有监控,有问题怎么办,任务堆积~加可视化界面。最好有智能调度,提高任务优先级。优先级高的任务先运行。资源调度,在机器数量不能满足需求的情况下,优先保证重要任务的执行。目前项目在一线,运行顺利~。