一种基于redis的异步延时队列实现方法与流程

文档序号:27553740发布日期:2021-11-24 23:32阅读:315来源:国知局
一种基于redis的异步延时队列实现方法与流程

1.本发明涉及微服务架构下java技术领域,具体提供一种基于redis的异步延时队列实现方法。


背景技术:

2.延迟队列是日常开发过程中,经常接触并需要使用到的一种技术方案。首先,队列是一种先进先出的数据结构。普通队列中的元素是有序的,先进入队列中的元素会被优先取出进行消费;
3.延时队列相比于普通队列最大的区别就体现在其延时的属性上,普通队列的元素是先进先出,按入队顺序进行处理,而延时队列中的元素在入队时会指定一个延迟时间,表示其希望能够在经过该指定时间后处理。从某种意义上来讲,延迟队列的结构并不像一个队列,而更像是一种以时间为权重的有序堆结构。
4.延迟队列有以下应用场景:
5.1.新建的订单,如果用户在15分钟内未支付,则自动取消。
6.2.公司的会议预定系统,在会议预定成功后,会在会议开始前半小时通知所有预定该会议的用户。
7.3.安全工单超过24小时未处理,则自动拉企业微信群提醒相关责任人。
8.4.用户下单外卖以后,距离超时时间还有10分钟时提醒外卖小哥即将超时。
9.目前已有的几种延时队列:
10.1.java中java.util.concurrent.delayqueue
11.优点:jdk自身实现,使用方便,量小适用;
12.缺点:队列消息处于jvm内存,不支持分布式运行和消息持久化;
13.2.rocketmq延时队列
14.优点:消息持久化,分布式;
15.缺点:不支持任意时间精度,只支持特定level的延时消息;
16.3.rabbitmq延时队列(ttl+dlx实现)
17.优点:消息持久化,分布式;
18.缺点:延时相同的消息必须扔在同一个队列。


技术实现要素:

19.本发明的技术任务是针对上述存在的问题,提供一种基于redis的异步延时队列实现方法,采用redis结合springboot作为延迟队的实现。
20.为实现上述目的,本发明提供了如下技术方案:
21.一种基于redis的异步延时队列实现方法,所述方法基于redis、springboot实现延迟队列,具体实现包括内容如下:
22.将需要处理的任务,按其需要延迟处理时间作为score加入到zset中,入队操作:
zadd key timestamp task;
23.redis的zadd的时间复杂度是o(logn),n是zset中元素个数,因此能相对比较高效的进行入队操作;
24.发起一个进程定时(比如每隔一秒)通过zreangebyscore方法查询zset中score最小的元素,具体操作为:zrangebyscore key

inf+inf limit 0 1withscores。
25.其中:springboot是由pivotal团队提供的全新框架,其设计目的是用来简化新spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。它默认配置了很多框架的使用方式,整合了所有的框架。
26.redis zadd命令redis有序集合(sorted set)redis zadd命令用于将一个或多个成员元素及其分数值加入到有序集当中。
27.redis有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。不同的是zset的每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序。
28.zadd语法:zadd key score member[score member

]作用:将一个或多个member元素及其score值加入到有序集合key中,如果member存在集合中,则更新值;score可以是整数或浮点数返回值:数字,新添加的元素个数。
[0029]
所述查询结果有两种情况:
[0030]
a.查询出的分数小于等于当前时间戳,说明到了这个任务需要执行的时间,则去异步处理该任务;
[0031]
b.查询出的分数大于当前时间戳,由于刚刚的查询操作取出来的是分数最小的元素,所以说明zset中所有的任务都还没有到需要执行的时间,则休眠一秒后继续查询;
[0032]
所述方法的实现包括内容:
[0033]
利用redis的setnx的互斥特性,把key当作锁,存储在redis中。但是用setnx需要解决死锁和正确解锁的问题。
[0034]
针对死锁问题,设置key

value的过期时间,并且使用lua脚本保证加锁和设置过期时间的原子性。
[0035]
针对解锁问题,将value设置为uuid,用对应的uuid去解锁,保证是加锁客户端进行对应的解锁操作。
[0036]
所述方法利用redis的list实现一个publisher推送消费,保证只被消费一次,这种不用考虑死锁问题,但是需要额外维护一个队列。
[0037]
所述zrangebyscore操作的时间复杂度为o(logn+m),其中n为zset中元素个数,m为查询的元素个数,因此定时查询操作也是比较高效的。
[0038]
所述方法的实现过程还包括:
[0039]
将延迟的消息任务通过hash算法路由至不同的redis key上,这样做有两大益处:
[0040]
a.避免了当一个key在存储了较多的延时消息后,入队操作以及查询操作速度变慢的问题(两个操作的时间复杂度均为o(logn))。
[0041]
b.系统具有了更好的横向可扩展性,当数据量激增时,我们可以通过增加redis key的数量来快速的扩展整个系统,来抗住数据量的增长。
[0042]
所述redis key对应建立一个处理进程,称为event进程,通过所述的
zrangebyscore方法轮询key,查询是否有待处理的延迟消息;
[0043]
所述event进程只负责分发消息,具体的业务逻辑通过一个额外的消息队列异步处理,这样做有2方面益处:
[0044]
a.一方面,event进程只负责分发消息,那么其处理消息的速度就会非常快,就不太会出现因为业务逻辑复杂而导致消息堆积的情况。
[0045]
b.另一方面,采用一个额外的消息队列后,消息处理的可扩展性也会更好,可以通过增加消费者进程数量来扩展整个系统的消息处理能力。
[0046]
所述event进程采用zookeeper选主单进程部署的方式,避免event进程宕机后,redis key中消息堆积的情况。一旦zookeeper的leader主机宕机,zookeeper会自动选择新的leader主机来处理redis key中的消息。
[0047]
与现有技术相比,本发明一种基于redis的异步延时队列实现方法具有以下突出的有益效果:
[0048]
本发明利用redis的特性,结合springboot,实现了一种延时队列的应用,简化了后端应用的开发,提高传统方式的系统性能,进而提高系统稳定性,开关的模式使其更加灵活;使用redis实现的队列具有很好的扩展性,可以很便捷的应对需求的变更和业务的扩展,在有大量的定时任务需要实现的时候,就可以考虑使用延迟队列去实现,让代码更具有扩展性。具体如下:
[0049]
1.消息传输可靠性:消息进入到延迟队列后,保证至少被消费一次。
[0050]
2.高可用性:至少得支持多实例部署。挂掉一个实例后,还有后备实例继续提供服务。
[0051]
3.实时性:允许存在一定的时间误差。
[0052]
4.支持消息删除:业务使用方,可以随时删除指定消息。
附图说明
[0053]
图1是本发明方法实现结构示意图。
具体实施方式
[0054]
下面将结合附图和实施例,对本发明作进一步详细说明。
[0055]
一种基于redis的异步延时队列实现方法,所述方法实现过程如下:
[0056]
1、入队操作:zadd key timestamp task,将需要处理的任务,按其需要延迟处理时间作为score加入到zset中,其中redis的zadd的时间复杂度是o(logn),n是zset中元素个数,因此能相对比较高效的进行入队操作。
[0057]
2、发起一个进程定时(比如每隔一秒)通过zreangebyscore方法查询zset中score最小的元素,具体操作为:zrangebyscore key

inf+inf limit 0 1withscores。查询结果有两种情况:
[0058]
a.查询出的分数小于等于当前时间戳,说明到这个任务需要执行的时间了,则去异步处理该任务;
[0059]
b.查询出的分数大于当前时间戳,由于刚刚的查询操作取出来的是分数最小的元素,所以说明zset中所有的任务都还没有到需要执行的时间,则休眠一秒后继续查询;
[0060]
c.利用redis的setnx的互斥特性,把key当作锁存在redis中,但是用setnx需要解决死锁和正确解锁的问题,通过以下方式解决:
[0061]
死锁:设置key

value的过期时间,并且使用lua脚本保证加锁和设置过期时间的原子性。
[0062]
解锁:解锁需要保证是加锁客户端进行解锁操作。将value设置为uuid,用对应的uuid去解锁保证是加锁客户端进行对应的解锁操作。
[0063]
d.利用redis的list实现一个publisher推送消费保证只被消费一次,这种方式不用考虑死锁问题,但是需要额外维护一个队列。
[0064]
e.同样的,zrangebyscore操作的时间复杂度为o(logn+m),其中n为zset中元素个数,m为查询的元素个数,因此定时查询操作也是比较高效的。
[0065]
如图1所示,将延迟的消息任务通过hash算法路由至不同的redis key上,这样做有两大益处:
[0066]
a.避免了当一个key在存储了较多的延时消息后,入队操作以及查询操作速度变慢的问题(两个操作的时间复杂度均为o(logn))。
[0067]
b.系统具有了更好的横向可扩展性,当数据量激增时,可以通过增加redis key的数量来快速的扩展整个系统,来抗住数据量的增长。
[0068]
每个redis key都对应建立一个处理进程,称为event进程,通过上述步骤2中所述的zrangebyscore方法轮询key,查询是否有待处理的延迟消息。
[0069]
所有的event进程只负责分发消息,具体的业务逻辑通过一个额外的消息队列异步处理,具有以下2方面的益处:
[0070]
a.一方面,event进程只负责分发消息,那么其处理消息的速度就会非常快,就不太会出现因为业务逻辑复杂而导致消息堆积的情况。
[0071]
b.另一方面,采用一个额外的消息队列后,消息处理的可扩展性也会更好,可以通过增加消费者进程数量来扩展整个系统的消息处理能力。
[0072]
event进程采用zookeeper选主单进程部署的方式,避免event进程宕机后,redis key中消息堆积的情况。一旦zookeeper的leader主机宕机,zookeeper会自动选择新的leader主机来处理redis key中的消息。
[0073]
以上所述的实施例,只是本发明较优选的具体实施方式,本领域的技术人员在本发明技术方案范围内进行的通常变化和替换都应包含在本发明的保护范围内。
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1