redisson延迟队列 redisson延迟队列丢失
SpringBoot操作Redis的各种实现(Jedis、Redisson的区别比较)
共同点:都提供了基于Redis操作的JavaAPI,不过封装程度,具体实现稍有不同。
不同点:
是Redis的Java实现的客户端。支持基本的数据类型如:String、Hash、List、Set、SortedSet。
特点:使用阻塞的I/O,办法调用同步,程序流需要等到socket处理完I/O才能执行,不支持异步操作。Jedis客户端实例不是线程安全的,需要经过连接池来使用Jedis。
优点点:分布式锁,分布式集合,可经过Redis支持延迟队列。
用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
基于Netty框架的事件驱动的通信层,其办法调用是异步的。Lettuce的API是线程安全的,所以能够操作单个Lettuce连接来完成各种操作。
maven配置引入,(要加上版本号,我这个地方是因为Parent已声明)
application-dev.yml
redisson-config.yml
或者,配置redisson-config.json
新建读取配置类
或者,在application.yml中配置如下
4.3.1丰富的jar支持,尤其是对NettyNIO框架
4.3.2丰富的配置机制选择,这个地方是详细的配置讲明
对于序列化机制中,就有不少
4.3.3API支持(部分展示),具体的Redis-->RedissonClient,可查看这个地方
4.3.4轻便的丰富的锁机制的实现
参考RedisTemplate配置。
redisson延迟队列redisson延迟队列丢失
另外,还需要额外的配置类
基于spring缓存实现
redisson应用(二)
延时队列能够实现一些需要延时处理的数据存储,比如延时发送邮件等.
缓存区满了,之后前面的数据将被丢弃
那个队列的offer办法会阻塞生产者直到有消费者消费了数据。并且提供了tryOffer办法和能够超时的tryOffer。
基于Redisson实现延迟队列
假设有如此一个场景,我们有一个订单,或者工单等等。需要在超时30分钟后进行关闭。那个时候我们最先想到的应该是采用定时任务去进行轮训推断,然而呢,每个订单的创建时刻是不一样的,那个时刻如何确定才好呢,5分钟。。1分钟。。执行一次吗。如此就会特别妨碍性能。且时刻误差很大。基于以上业务需要我们想到了有以下解决方案。
我们首先来回顾下JDK的延迟队列
基于延迟队列要实现接口Delayed,同时实现getDelay办法和compareTo办法
订单的实体,为了简单就定义基础几个字段。
为了简单我们暂且定义延迟时刻为10s
输出结果
2022-07-01T15:00
当前时刻:2022-07-01T15:10:37.375
固然今天的主角是它了,我们要紧围绕着基于Redisson的延迟队列来讲。
事实上Redisson延迟队列内部也是基于redis来实现的,我们先来进行整合使用看看效果。基于springboot
1.依赖:
2.创建redisson.yml
3.创建配置类RedissonConfig,这个地方是为了读取我们刚刚创建在配置文件中的yml
4.测试
操纵台输出:
订单生成时刻2022-07-01T15:22:10.304
订单关闭时刻2022-07-01T15:22:20.414
我们首先来了解两个API
那么什么原因会涉及到两个队列呢,这两个队列到底有什么用呢?
首先我们实际操作的是RBlockingQueue阻塞队列,并不是RDelayedQueue队列,RDelayedQueue对接要紧是提供中间转发的一个队列,类似中间商的意思
画个小图理解下
这个地方不难看出我们基本上基于RBlockingQueue目标队列在进行消费,而RDelayedQueue算是会把过期的消息放入到我们的目标队列中
我们只要从RBlockingQueue队列中取数据即可。
看起来依然不够深入,我们继续看。我们明白Redisson是基于redis来实现的那么我们看看里面到底做了什么事
打开redis客户端,执行monitor命令,看下在执行上面订单操作时redis到底执行了哪些命令
monitor命令能够看到操作redis时执行了什么命令
这个地方参考:
我们明白Zset是按照分数升序的也算是最小的分数在最前面,基于那个特点,大致知道,利用过期时刻的时刻戳作为分数放入到Zset中,那么即将过期的就在最上面。
直截了当上个图解
Redis使用zset有序集合做延迟队列
把所有需要在将来执行的任务都添加到有序集合里面,并将任务的执行时刻设置为分值,另外再使用一个进程来查找有序集合里面是否存在能够马上执行的任务,假如有的话,就从有序集合里面移除这个任务,并将它添加到适当的任务队列里面。
--出自《Redis实战》
创建函数addFutureJob,负责将延迟任务添加到有序集合job中。
有序集合里存储的元素,能够使用json格式保存。
内部结构能够类似如下这种:
参数:
$job:存储延迟任务的有序集合的名字,叫job
$queue:当任务到达执行时刻时,转存到具体的队列里执行
$fun:负责执行的函数名称或匿名函数
redisson延迟队列redisson延迟队列丢失
$time:延迟任务执行的具体时刻
$parameter:传递的参数
执行脚本后,将存储到job那个有序集合里
另一个脚本中,读取job集合,检查是否有需要执行的任务
那个函数getQueue()的基本流程是如此:
(1)依照分数从小到大排列,读取第一个元素。假如元素不存在返回false
(2)假如元素任务存在,同时它的分数(执行时刻)小于等于当前时刻,讲明那个任务能够执行了。
(3)json转化成数组,读取任务的queue参数,将它添加到指定的队列里,然后从job中删除那个任务。
(4)上述转移操作时,假如成功,记录日志。while接着循环检查job有序集合
(5)假如转移操作失败,返回false
(6)后续没有要执行的任务时,停止循环,返回false
此处循环读取也能够使用zrangeByScore()函数,依照分数范围进行读取返回集合内的指定元素。
经过redis的有序集合[zset]实现延迟队列
php使用redis的有序集合zset实现延迟队列
我们经过redis的有序集合zset来实现简单的延迟队列,将消息数据序列化,作为zset的基本元素,把消息生产时刻戳+消息处理延迟时刻戳作为score,每次经过zRangeByScore获取一条消息进行处理,后经过zRem删除集合元素:相当于移除需要消费的Job。
优点:
缺点:
1.不适合延迟时刻高的业务场景。延迟时刻可能有几秒钟的误差。
2.不适合大型项目,大型项目建议使用rabbitmq的延迟i消息队列
下面是简单的实现demo
建议不要使用,redis过期监听实现关闭订单
在电商、支付等领域,往往会有如此的场景,用户下单后放弃支付了,那这笔订单会在指定的时刻段后进行关闭操作,细心的你一定发现了像某宝、某东都有如此的逻辑,而且时刻很准确,误差在1s内;那他们是如何实现的呢?
普通实现的办法有几种:
有一些方案尽管广为流传但存在着致命缺陷,不要用来实现延时任务
在Redis官方手册的keyspace-notifications:timing-of-expired-events中明确指出:
redis自动过期的实现方式是:定时任务离线扫描并删除部分过期键;在访咨询键时惰性检查是否过期并删除过期键。redis从未保证会在设定的过期时刻马上删除并发送过期通知。实际上,过期通知晚于设定的过期时刻数分钟的事情也比较常见。
这是一种比定时扫描数据库更“LOW”的解决方案,请不要使用。
有另一位大佬做了测试请勿过度依赖Redis的过期监听,有兴趣的朋友能够自行查阅。
redisson延迟队列redisson延迟队列丢失
死信(DeadLetter)是rabbitmq提供的一种机制。当一条消息满足下列条件之一那么它会成为死信:
若配置了死信队列,死信会被rabbitmq投到死信队列中。
在rabbitmq中创建死信队列的操作流程似乎是:
死信队列的设计目的是为了存储没有被正常消费的消息,便于排查和重新投递。死信队列同样也没有对投递时刻做出保证,在第一条消息成为死信之前,后面的消息即使过期也可不能投递为死信。
为了解决那个咨询题,rabbit官方推出了延迟投递插件rabbitmq-delayed-message-exchange,推举使用官方插件来做延时消息。
时刻轮是一种很优秀的定时任务的数据结构,但是绝大多数时刻轮实现是纯内存没有持久化的。运行时刻轮的进程崩溃之后其中所有的任务都会灰飞烟灭,所以奉劝各位勇士慎重使用。
redissondelayqueue是一种基于rediszset结构的延时队列实现。delayqueue中有一个名为timeoutSetName的有序集合,其中元素的score为投递时刻戳。delayqueue会定时使用zrangebyscore扫描已到投递时刻的消息,然后把它们挪移到就绪消息列表中。
delayqueue保证redis不崩溃的事情下可不能丢失消息,在没有更好的解决方案时不妨一试。
在数据库索引设计良好的事情下,定时扫描数据库中未完成的订单产生的开销并没有想象中那么大。在使用redissondelayqueue等定时任务中间件时能够并且使用扫描数据库的办法作为补偿机制,避免中间件故障造成任务丢失。
文章来自https://
(二)连接Redis,Lettuce与Jedis客户端
Redis优势
从SpringBoot2.x开始Lettuce已取代Jedis成为首选Redis的客户端。固然SpringBoot2.x仍然支持Jedis,同时你能够任意切换客户端。
使用Lecttuce需要再引用一个包
配置
org.springframework.boot.autoconfigure.data.redis.RedisProperties中集成了Jedis与Lettuce。
概念:
Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
Redisson:实现了分布式和可扩展的Java数据结构。
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
优点:
Jedis:比较全面的提供了Redis的操作特性
Redisson:促使使用者对Redis的关注分离,提供不少分布式相关操作服务,例如,分布式锁,分布式集合,可经过Redis支持延迟队列
Lettuce:要紧在一些分布式缓存框架上使用比较多
可伸缩:
Jedis:使用阻塞的I/O,且其办法调用基本上同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要经过连接池来使用Jedis。
Redisson:基于Netty框架的事件驱动的通信层,其办法调用是异步的。Redisson的API是线程安全的,所以能够操作单个Redisson连接来完成各种操作
Lettuce:基于Netty框架的事件驱动的通信层,其办法调用是异步的。Lettuce的API是线程安全的,所以能够操作单个Lettuce连接来完成各种操作
来源:今日热点