最近,又重新学习了下Redis,深深被Redis的魅力所折服,我才知道Redis不仅能快还能慢(我想也这么优秀o(╥﹏╥)o),简直是个利器呀。

咳咳咳,大家不要误会,本文很正经的啦!

好了,接下来回到我们的话题,我们都知道Redis是一种基于内存的单进程单线程数据库(Redis6.0开始之后支持多线程啦!),处理速度都非常快。那么为何Redis又能慢呢?原来,这里说的慢是指Redis可以设置一些参数达到慢处理的结果。(这就是为什么Redis既能快又能慢啦!)

那接下来开始讲讲我们的楷模Redis在队列中如何实现延时的情况:

在我们日常生活中,我们可以发现,

  • 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消。
  • 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单。
  • 点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单。
  • 收快递的时候,如果我们没有点确认收货,在一段时间后程序会自动完成订单。
  • 在平台完成订单后,如果我们没有在规定时间评论商品,会自动默认买家不评论。
  • .......还有很多这样的场景。

这时,我们可以想想为什么要这样做?

因为这样可以保证商品的库存可以释放给其他人购买,你可以不用一直等待打车却得不到回复,你可以及时换一家店点到外卖。

那么这些情况都是如何实现的呢?

这时我们可以看看这个图,来看看消息延迟是如何处理的:

当用户发送一个消息请求给服务器后台的时候,服务器会检测这条消息是否需要进行延时处理,如果需要就放入到延时队列中,由延时任务检测器进行检测和处理,对于不需要进行延时处理的任务,服务器会立马对消息进行处理,并把处理后的结果相应返会给用户。

对于在延时任务检测器内部的话,有查询延迟任务和执行延时任务两个职能,任务检测器会先去延时任务队列进行队列中信息读取,判断当前队列中哪些任务已经时间到期并将已经到期的任务输出执行(具有实时性,会存在一定的时间误差,因为这个是定时任务)。

这时,我们可以想一想在Redis的数据结构中有哪些能进行时间设置标志的命令?

是不是想到的 zset 这个命令,具有去重有序(分数排序)的功能。没错,你想对了呀!

我们可以使用 zset(sortedset)这个命令,用设置好的时间戳作为score进行排序,使用 zadd score1 value1 ....命令就可以一直往内存中生产消息。再利用 zrangebysocre 查询符合条件的所有待处理的任务,通过循环执行队列任务即可。也可以通过 zrangebyscore key min max withscores limit 0 1 查询最早的一条任务,来进行消费。

总的来说,你可以通过以下两种方式来实现((*^▽^*)如果你想到其他方法,也可以告诉我下呀~):

(1)使用zrangebyscore来查询当前延时队列中所有任务,找出所有需要进行处理的延时任务,在依次进行操作。

(2)查找当前最早的一条任务,通过score值来判断任务执行的时候是否大于了当前系统的时候,比如说:最早的任务执行时间在3点,系统时间在2点58分),表示这个应该需要立马被执行啦,时间快到了(冲冲冲,他来了他来了,他带着死神的步伐来了)。

我们可以想一想Redis来实现延时队列有何优势呢?

其实,Redis用来进行实现延时队列是具有这些优势的:

(1)Redis zset支持高性能的 score 排序。

(2)Redis是在内存上进行操作的,速度非常快。

(3)Redis可以搭建集群,当消息很多时候,我们可以用集群来提高消息处理的速度,提高可用性。

(4)Redis具有持久化机制,当出现故障的时候,可以通过AOF和RDB方式来对数据进行恢复,保证了数据的可靠性

这时候,会有小伙伴问了还有没有其他实现延时队列的方式呀!emmm....当然有的,只有想不到的没有做不到(O(∩_∩)O哈哈~,开玩笑)

一、用消息中间件实现延时队列

(1)通过 RabbitMQ 来实现延时队列,也就是用消息中间件来实现延时队列啦~

方法一:在MQ中我们可以对Queue设置 x-expires 过期时间或者对 Message设置超时时间x-message-ttl。

(这里要注意下:延时相同的消息我们要扔到同一个队列中,对于每一个延时要建立一个与之对应的队列—这是由于MQ的过期检测是惰性检测的。)

方法二:我们可以用RabbitMQ的插件rabbitmq-delayed-message-exchange插件来实现延时队列。达到可投递时间时并将其通过x-delayed-type类型标记的交换机类型投递至目标队列。

(2)RocketMQ实现延时队列

rocketmq在发送延时消息时,是先把消息按照延迟时间段发送到指定的队列中(把延时时间段相同的消息放到同一个队列中,保证了消息处理的顺序性,可以让同一个队列中消息延时时间是相同的,整个RocketMQ中延时消息时按照递增顺序排序,保证信息处理的先后顺序性。)。之后,通过一个定时器来轮询处理这些队列里的信息,判断是否到期。对于到期的消息会发送到相应的处理队列中,进行处理。

哈哈,目前RocketMQ只支持特定的延时时间段,1s,5s,10s,...2h,不能支持任意时间段的延时设置。有兴趣的小伙伴可以去了解下它是相关知识呀~

二、Kafka实现延时队

Kafka基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer),Kafka中的时间轮(TimingWheel)是一个存储定时任务的环形队列,可以进行相关的延时队列设置。

三、Netty实现延时队列

Netty也有基于时间轮算法来实现延时队列。Netty在构建延时队列主要用HashedWheelTimer,HashedWheelTimer底层数据结构是使用DelayedQueue,采用时间轮的算法来实现。

四、DelayQueue来实现延时队列

Java中有自带的DelayQueue数据类型,我们可以用这个来实现延时队列。DelayQueue是封装了一个PriorityQueue(优先队列),在向DelayQueue队列中添加元素时,会给元素一个Delay(延迟时间)作为排序条件,队列中最小的元素会优先放在队首,对于队列中的元素只有到了Delay时间才允许从队列中取出。这种实现方式是数据保存在内存中,可能面临数据丢失的情况,同时它是无法支持分布式系统的。

相关代码我之后会上传到我的GitHub上呀~(冲冲冲,小伙伴们!)又是美好的一天。

你知道Redis可以实现延迟队列吗?的更多相关文章

  1. 灵感来袭,基于Redis的分布式延迟队列(续)

    背景 上一篇(灵感来袭,基于Redis的分布式延迟队列)讲述了基于Java DelayQueue和Redis实现了分布式延迟队列,这种方案实现比较简单,应用于延迟小,消息量不大的场景是没问题的,毕竟J ...

  2. PHP基于Redis实现轻量级延迟队列

    延迟队列,顾名思义它是一种带有延迟功能的消息队列. 那么,是在什么场景下我才需要这样的队列呢? 一.背景 先看看一下业务场景: 1.会员过期前3天发送召回通知 2.订单支付成功后,5分钟后检测下游环节 ...

  3. 灵感来袭,基于Redis的分布式延迟队列

    延迟队列 延迟队列,也就是一定时间之后将消息体放入队列,然后消费者才能正常消费.比如1分钟之后发送短信,发送邮件,检测数据状态等. Redisson Delayed Queue 如果你项目中使用了re ...

  4. php使用redis的有序集合zset实现延迟队列

    延迟队列就是个带延迟功能的消息队列,相对于普通队列,它可以在指定时间消费掉消息. 延迟队列的应用场景: 1.新用户注册,10分钟后发送邮件或站内信. 2.用户下单后,30分钟未支付,订单自动作废. 我 ...

  5. 基于Redis实现延迟队列

    背景 在后端服务中,经常有这样一种场景,写数据库操作在异步队列中执行,且这个异步队列是多进程运行的,这时如果对同一资源进行写库操作,很有可能产生数据被覆盖等问题,于是就需要业务层在更新数据库之前进行加 ...

  6. Redis(二)延迟队列

    1.目录 延迟队列 进一步优化 2.延迟队列 package com.redis; import java.lang.reflect.Type; import java.util.Set; impor ...

  7. redis(六)---- 简单延迟队列

    延迟队列的应用场景也很常见,例如:session的超时过期.自动取消未付款订单等等.redis中有一种数据结构叫做zset,即有序集合.元素类型为String类型,且元素具有唯一性不能重复,每个元素可 ...

  8. Redis实现延迟队列的正确姿势

    在之前探讨延时队列的文章中我们提到了 redisson delayqueue 使用 redis 的有序集合结构实现延时队列,遗憾的是 go 语言社区中并无类似的库.不过问题不大,没有轮子我们自己造. ...

  9. Java 延迟队列使用

    延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到…… 应用场景比较多,比如延时1分钟发短 ...

随机推荐

  1. Java实现 蓝桥杯VIP 基础练习 分解质因数

    题目介绍 问题描述 求出区间[a,b]中所有整数的质因数分解. 输入格式 输入两个整数a,b. 输出格式 每行输出一个数的分解,形如k=a1a2a3-(a1<=a2<=a3-,k也是从小到 ...

  2. Java实现 蓝桥杯VIP 基础练习 高精度加法

    java算法 蓝桥杯 高精度加法 问题描述 在C/C++语言中,整型所能表示的范围一般为-231到231(大约21亿),即使long long型,一般也只能表示到-263到263.要想计算更加规模的数 ...

  3. Java实现 LeetCode 319 灯泡开关

    319. 灯泡开关 初始时有 n 个灯泡关闭. 第 1 轮,你打开所有的灯泡. 第 2 轮,每两个灯泡你关闭一次. 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭).第 i 轮,每 ...

  4. python IDE pycharm的安装与使用

    Python开发最牛逼的IDE——pycharm (其实其它的工具,例如eclipse也可以写,只不过比较麻烦,需要安装很多的插件,所以说pycharm是最牛逼的) pycharm,下载专业版的,不要 ...

  5. Mysql(Mariadb)数据库主从

    Mysql主从复制的实现原理图大致如下: MySQL之间数据复制的基础是以二进制日志文件(binary log file)来实现的,一台MySQL数据库一旦启用二进制日志后,其作为master,它数据 ...

  6. “进大厂大半年,每个月都想走!”大公司 VS 小公司到底该如何选择?

    前言 江湖风云不断,有人吐槽阿里996,也有人吐槽华为狼性文化,这不,就看到有腾讯员工吐槽“进腾讯大半年,每个月都想走!” “和我一样,进去一周就想走”.“我都陷入自我怀疑了,以为自己适应不了大公司” ...

  7. MacOS配置.bash_profile,重启终端后配置失效和MacOS .zshrc does not exist问题

    MacOS配置.bash_profile,重启终端后配置失效和MacOS .zshrc does not exist问题 场景 ​ 在Mac中配置golang环境变量更改GOPATH路径,在~/.ba ...

  8. Telegraf和Grafana监控多平台上的SQL Server-自定义监控数据收集

    问题 在上一篇文章中,我们使用Telegraf自带的Plugin配置好了的监控,但是自带的Plugin并不能完全覆盖我们想要的监控指标,就需要收集额外的自定义的监控数据,实现的方法有: 开发自己的Te ...

  9. [转] CISC与RISC

    点击阅读原文      这里就不去管细节,简单来谈一下,ARM和X86之间为什么不太具有可比性的问题.要搞清楚这个问题首先要明白什么是架构,之前也有很多人提到了架构不同,但架构是什么意思?它是一个比较 ...

  10. Docker学习 ,超全文档!

    我们的口号是:再小的帆也能远航,人生不设限!!        一.学习规划: Docker概述 Docker安装 Docker命令 Docker镜像 镜像命令 容器命令 操作命令 容器数据卷  Doc ...