浅谈我是如何用redis做实时订阅推送的

前阵子开发了公司领劵中心的项目,这个项目是以redis作为关键技术落地的。

先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了。。。

其中有一个功能叫做领劵的订阅推送。什么是领劵的订阅推送?就是用户订阅了该劵的推送,在可领取前的一分钟就要把提醒信息推送到用户的app中。本来这个订阅功能应该是消息中心那边做的,但他们说这个短时间内做不了。所以让我这个负责优惠劵的做了-.-!。具体方案就是到具体的推送时间点了,coupon系统调用消息中心的推送接口,把信息推送出去。

下们我们分析一下这个功能的业务情景。公司目前注册用户6000W+,是哪家就不要打听了。。。比如有一张无门槛的优惠劵下单立减20元,那么抢这张劵的人就会比较多,我们保守估计10W+,百万级别不好说。我们初定为20W万人,那么这20W条推送信息要在一分钟推送完成!并且一个用户是可以订阅多张劵的。所以我们知道了这个订阅功能的有两个突出的难点:

1、推送的实效性:推送慢了,用户会抱怨没有及时通知他们错过了开抢时机。

2、推送的体量大:爆款的神劵,人人都想抢!

然而推送体量又会影响到推送的实效性。这真是一个让人头疼的问题!

那就让我们把问题一个个解决掉吧!

推送的实效性的问题:当用户在领劵中心订阅了某个劵的领取提醒后,在后台就会生成一条用户的订阅提醒记录,里面记录了在哪个时间点给用户发送推送信息。所以问题就变成了系统如何快速实时选出哪些要推送的记录!

方案1:MQ的延迟投递。

MQ虽然支持消息的延迟投递但尺度太大1s 5s 10s 30s 1m,用来做精确时间点投递不行!并且用户执行订阅之后又取消订阅的话,要把发出去的MQ消息delete掉这个操作有点头大,短时间内难以落地!并且用户可以取消之后再订阅,这又涉及到去重的问题。所以MQ的方案否掉。

方案2:传统定时任务。

这个相对来说就简单一点,用定时任务是去db里面load用户的订阅提醒记录,从中选出当前可以推送的记录。但有句话说得好任何脱离实际业务的设计都是耍流氓~。下面我们就分析一下传统的定时任务到底适不适合我们的这个业务!

能否支持多机同时跑 一般不能,同一时刻只能单机跑。
存储数据源 一般是mysql或者其它传统数据库,并且是单表存储
频率 支持秒、分、时、天,一般不能太快

总上所述我们就知道了一般传统的定时任务存在以下缺点:

1、性能瓶颈。只有一台机在处理,在大体量数据面前力不从心!

2、实效性差。定时任务的频率不能太高,太高会业务数据库造成很大的压力!

3、单点故障。万一跑的那台机挂了,那整个业务不可用了-。- 这是一个很可怕的事情!

所以传统定时任务也不太适合这个业务。。。

那我们是不是就束手无策了呢?其实不是的! 我们只要对传统的定时任务做一个简单的改造!就可以把它变成可以同时多机跑,并且实效性可以精确到秒级,并且拒绝单点故障的定时任务集群!这其中就要借助我们的强大的redis了。

方案3:定时任务集群

首先我们要定义定时任务集群要解决的三个问题!

1、实效性要高

2、吞吐量要大

3、服务要稳定,不能有单点故障

下面是整个定时任务集群的架构图。

架构很简单:我们把用户的订阅推送记录存储到redis集群的sortedSet队列里面,并且以提醒用户提醒时间戳作为score值,然后在我们个每业务server里面起一个定时器频率是秒级,我的设定就是1s,然后经过负载均衡之后从某个队列里面获取要推送的用户记录进行推送。下面我们分析以下这个架构

1、性能:除去带宽等其它因素,基本与机器数成线性相关。机器数量越多吞吐量越大,机器数量少时相对的吞吐量就减少。

2、实效性:提高到了秒级,效果还可以接受。

3、单点故障?不存在的!除非redis集群或者所有server全挂了。。。。

这里解析一下为什么用redis?

第一redis 可以作为一个高性能的存储db,性能要比MySQL好很多,并且支持持久化,稳定性好。

第二redisSortedSet队列天然支持以时间作为条件排序,完美满足我们选出要推送的记录。

ok~既然方案已经有了那如何在一天时间内把这个方案落地呢?是的我设计出这个方案到基本编码完成,时间就是一天。。。 因为时间太赶鸟。

首先我们以user_id作为key,然后mod队列数hash到redis SortedSet队列里面。为什么要这样呢,因为如果用户同时订阅了两张劵并且推送时间很近,这样的两条推送就可以合并成一条~,并且这样hash也相对均匀。下面是部分代码的截图:

然后要决定队列的数量,一般正常来说我们有多少台处理的服务器就定义多少条队列。因为队列太少,会造成队列竞争,太多可能会导致记录得不到及时处理。

然而最佳实践是队列数量应该是可动态配置化的,因为线上的集群机器数是会经常变的。大促的时候我们会加机器是不是,并且业务量增长了,机器数也是会增加是不是~。所以我是借用了淘宝的diamond进行队列数的动态配置。

我们每次从队列里面取多少条记录也是可以动态配置的

这样就可以随时根据实际的生产情况调整整个集群的吞吐量~。 所以我们的定时任务集群还是具有一个特性就是支持动态调整~。

最后一个关键组件就是负载均衡了。这个是非常重要的!因为这个做得不好就会可能导致多台机竞争同时处理一个队列,影响整个集群的效率!在时间很紧的情况下我就用了一个简单实用的利用redis一个自增key 然后 mod 队列数量算法。这样就很大程度上就保证不会有两台机器同时去竞争一条队列~.

最后我们算一下整个集群的吞吐量

10(机器数) * 2000(一次拉取数) = 20000。然后以MQ的形式把消息推送到消息中心,发MQ是异步的,算上其它处理0.5s。

其实发送20W的推送也就是10几s的事情。

ok~ 到这里我们整个定时任务集群就差不多基本落地好了。如果你问我后面还有什么可以完善的话那就是:

1、加监控, 集群怎么可以木有监控呢,万一出问题有任务堆积怎么办~

2、加上可视化界面。

3、最好有智能调度,增加任务优先级。优先级高的任务先运行嘛。

4、资源调度,万一机器数量不够,力不从心,优先保证重要任务执行。

目前项目已上前线,运行平稳~。

到此这篇关于浅谈我是如何用redis做实时订阅推送的的文章就介绍到这了,更多相关redis 实时订阅推送内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis 订阅发布_Jedis实现方法

    我想到使用Redis的订阅发布模式是用来解决推送问题的-. 对于概念性的叙述,多多少少还是要提一下的: 什么是Redis发布订阅?Redis发布订阅是一种消息通信模式,发送者通过通道A发送消息message,订阅过通道A的客户端就可以接收到消息message.嗯度娘上面的解释要比我所说的好多了,而我所理解的就是:所谓的订阅发布模式,其实和我们看电视,听广播差不多,在我们没有调台(换频道)的时候,那个频道也是在传递消息的(发布).我们换到那个频道上(订阅)就能接收到消息了.是的,虽然可能有些不恰当

  • Redis发布订阅和实现.NET客户端详解

    前言 发布订阅在设计模式中也可以说是观察者模式,针对这个模式是处理对象间一对多的依赖关系的,当一个对象发生变化,其它依赖他的对象都要得到通知并更新. 然而它也有自己的缺点,就是当主题发生一系列的变化时,观察者都要做批量的更新,如果这样的更新成本很高,那么解决方法就是根据种类需求通知,而不能盲目的通知所有的观察者. 那针对这个缺点,一般的情况下,你没有需求谁订阅一个跟自己无关的消息推送呢?这也正好说明推送的消息需要整理而不能一窝蜂的什么消息都往一个通道里面抛,要分而治之,合理的设计发布通道的用途,

  • redis发布订阅Java代码实现过程解析

    前言 Redis除了可以用作缓存数据外,另一个重要用途是它实现了发布订阅(pub/sub)消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. 为了实现redis的发布订阅机制,首先要打开redis服务:其次,引入redis需要的jar包,在pom.xml配置文件加入以下代码: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> &

  • redis发布订阅_动力节点Java学院整理

    其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果博主发表了文章,那么100个人就会同时收到通知邮件,除了这个场景还能找到其他场景么,当然有啦,你想想,如果你要在内存里面做一个读写分离的程序,为了维持数据的完整性,你是不是需要保证在写入的时候,也要分发到各个读内存的程序中呢?所以说场景还是很多的,在于你的挖掘~~~ 下面还是从基本命令入手: 一:命令简介 从redis手册上面可以看到,其实"发布.订阅"

  • redis发布和订阅_动力节点Java学院整理

    Redis 的 pub sub实现了邮件系统,发送者(在 Redis 术语中被称为发布者)发送的邮件,而接收器(用户)接收它们.由该消息传送的链路被称为信道. Redis客户端可以订阅任何数目的通道. 例子 以下举例说明如何发布用户的概念工作.在下面的例子给出一个客户端订阅一个通道名为redisChat redis 127.0.0.1:6379> SUBSCRIBE redisChat Reading messages... (press Ctrl-C to quit) 1) "subsc

  • 浅谈我是如何用redis做实时订阅推送的

    前阵子开发了公司领劵中心的项目,这个项目是以redis作为关键技术落地的. 先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了... 其中有一个功能叫做领劵的订阅推送.什么是领劵的订阅推送?就是用户订阅了该劵的推送,在可领取前的一分钟就要把提醒信息推送到用户的app中.本来这个订阅功能应该是消息中心那边做的,但他们说这个短时间内做不了.所以让我这个负责优惠劵的做了-.-!.具体方案就是到具体的推送时间点了,coupon系统调用消息中心的推送接口,把信

  • 浅谈内存耗尽后Redis会发生什么

    前言 作为一台服务器来说,内存并不是无限的,所以总会存在内存耗尽的情况,那么当 Redis 服务器的内存耗尽后,如果继续执行请求命令,Redis 会如何处理呢? 内存回收 使用Redis 服务时,很多情况下某些键值对只会在特定的时间内有效,为了防止这种类型的数据一直占有内存,我们可以给键值对设置有效期.Redis 中可以通过 4 个独立的命令来给一个键设置过期时间: expire key ttl:将 key 值的过期时间设置为 ttl 秒. pexpire key ttl:将 key 值的过期时

  • 浅谈MySQL如何优雅的做大表删除

    随着时间的推移或者业务量的增长,数据库空间使用率也不断的呈稳定上升状态,当数据库空间将要达到瓶颈的时候,可能我们才会发现数据库有那么一两张的超级大表!他们堆积了从业务开始到现在的全部数据,但是90%的数据都是没有业务价值的,这时候该如何处理这些大表? 既然是没有价值的数据,我们通常一般会选择直接删除或者归档后删除两种,对于数据删除的操作方式来说又可分为两大类: 通过truncate直接删除表中全部数据 通过delete删除表中满足条件记录 一.Truncate操作 从逻辑意义上来讲,trunca

  • 浅谈java如何实现Redis的LRU缓存机制

    目录 LRU概述 使用LinkedHashMap实现 使用LinkedHashMap简单方法实现 双链表+hashmap LRU概述 最近使用的放在前面,最近没用的放在后面,如果来了一个新的数,此时内存满了,就需要把旧的数淘汰,那为了方便移动数据,肯定就得使用链表类似的数据结构,再加上要判断这条数据是不是最新的或者最旧的那么应该也要使用hashmap等key-value形式的数据结构. 使用LinkedHashMap实现 package thread; import java.util.Link

  • 浅谈为什么单线程的redis那么快

    目录 redis单机QPS 为什么这么快 内存型数据库 简单的数据结构 单线程 IO多路复用 总结 redis单机QPS ./redis-benchmark -t set,lpush -n 100000 -q SET: 82101.80 requests per second LPUSH: 82440.23 requests per second 在自己的电脑上测试SET和LPUSH10万次,可以发现每秒SET和LPUSH大概在8w多,接近官方说的单机10w qps的写. 为什么这么快 内存型数

  • 浅谈一下如何保证Redis缓存与数据库的一致性

    目录 1.四种同步策略: 2.更新缓存还是删除缓存 2.1 更新缓存 2.2 删除缓存 3.先操作数据库还是缓存 3.1 先删除缓存再更新数据库 3.2 先更新数据库再删除缓存 最终结论: 4.延时双删 4.1 采用读写分离的架构怎么办? 5.利用消息队列进行删除的补偿 1.四种同步策略: 想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略: 先更新缓存,再更新数据库: 先更新数据库,再更新缓存: 先删除缓存,再更新数据库: 先更新数据库,再删除缓存. 从这4种同步策略中,我们需要作

  • php实现websocket实时消息推送

    php实现websocket实时消息推送,供大家参考,具体内容如下 SocketService.php <?php /** * Created by xwx * Date: 2017/10/18 * Time: 14:33 */ class SocketService { private $address = '0.0.0.0'; private $port = 8083; private $_sockets; public function __construct($address = '',

  • 前端与RabbitMQ实时消息推送未读消息小红点实现示例

    目录 引言 RabbitMQ 搭建 1.开启 mqtt 协议 服务端消息发送 1.mqtt 客户端依赖包 2.消息发送者 前端消息订阅 测试 总结 引言 前几天粉丝群里有个小伙伴问过:web 页面的未读消息(小红点)怎么实现比较简单,刚好本周手头有类似的开发任务,索性就整理出来供小伙伴们参考,没准哪天就能用得上呢. 之前在 <springboot + rabbitmq 做智能家居> 中说过可以用 rabbitmq 的 MQTT 协议做智能家居的指令推送,里边还提到过能用 MQTT 协议做 we

  • java实现web实时消息推送的七种方案

    目录 引言 什么是消息推送(push) 短轮询 长轮询 iframe流 SSE (我的方式) MQTT Websocket 自定义推送 Github地址 引言 做了一个小破站,现在要实现一个站内信web消息推送的功能,对,就是下图这个小红点,一个很常用的功能. 不过他还没想好用什么方式做,这里我帮他整理了一下几种方案,并简单做了实现. 案例下载 什么是消息推送(push) 推送的场景比较多,比如有人关注我的公众号,这时我就会收到一条推送消息,以此来吸引我点击打开应用. 消息推送(push)通常是

  • asp.net mvc实现简单的实时消息推送

    因为项目需要,需要在网页上实现消息的推送.在百度上搜索了一下,发现实现网页上的消息推送,可以使用asp.net 中的SignalR类库,当然也可以使用H5的WebSocket  Ajax的轮回.当然此处我们使用asp.net 中的SignalR类库.因为它可以实现网页上消息的实时推送.什么是实时推送呢,我简单的说一下我个人的理解吧.实时:在同一时间类发生的事情,当然在计算机中并不是绝对的实时,因为CPU在同一时间片只能处理一个任务,那么这个时候疑问又来了? 我们平时使用电脑又上网,又听音乐是如何

随机推荐