redis实现简单延时队列(转)
继之前用rabbitMQ实现延时队列,Redis由于其自身的Zset数据结构,也同样可以实现延时的操作
Zset本质就是Set结构上加了个排序的功能,除了添加数据value之外,还提供另一属性score,这一属性在添加修改元素时候可以指定,每次指定后,Zset会自动重新按新的值调整顺序。可以理解为有两列字段的数据表,一列存value,一列存顺序编号。操作中key理解为zset的名字,那么对延时队列又有何用呢?试想如果score代表的是想要执行时间的时间戳,在某个时间将它插入Zset集合中,它变会按照时间戳大小进行排序,也就是对执行时间前后进行排序,这样的话,起一个死循环线程不断地进行取第一个key值,如果当前时间戳大于等于该key值的socre就将它取出来进行消费删除,就可以达到延时执行的目的, 注意不需要遍历整个Zset集合,以免造成性能浪费。
Zset的排列效果如下图:

java代码实现如下:
package cn.chinotan.service.delayQueueRedis; import org.apache.commons.lang3.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple; import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; /**
* @program: test
* @description: redis实现延时队列
* @author: xingcheng
* @create: 2018-08-19
**/
public class AppTest { private static final String ADDR = "127.0.0.1";
private static final int PORT = 6379;
private static JedisPool jedisPool = new JedisPool(ADDR, PORT);
private static CountDownLatch cdl = new CountDownLatch(10); public static Jedis getJedis() {
return jedisPool.getResource();
} /**
* 生产者,生成5个订单
*/
public void productionDelayMessage() {
for (int i = 0; i < 5; i++) {
Calendar instance = Calendar.getInstance();
// 3秒后执行
instance.add(Calendar.SECOND, 3 + i);
AppTest.getJedis().zadd("orderId", (instance.getTimeInMillis()) / 1000, StringUtils.join("000000000", i + 1));
System.out.println("生产订单: " + StringUtils.join("000000000", i + 1) + " 当前时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println((3 + i) + "秒后执行");
}
} //消费者,取订单
public static void consumerDelayMessage() {
Jedis jedis = AppTest.getJedis();
while (true) {
Set<Tuple> order = jedis.zrangeWithScores("orderId", 0, 0);
if (order == null || order.isEmpty()) {
System.out.println("当前没有等待的任务");
try {
TimeUnit.MICROSECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
Tuple tuple = (Tuple) order.toArray()[0];
double score = tuple.getScore();
Calendar instance = Calendar.getInstance();
long nowTime = instance.getTimeInMillis() / 1000;
if (nowTime >= score) {
String element = tuple.getElement();
Long orderId = jedis.zrem("orderId", element);
if (orderId > 0) {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ":redis消费了一个任务:消费的订单OrderId为" + element);
}
}
}
} static class DelayMessage implements Runnable{
@Override
public void run() {
try {
cdl.await();
consumerDelayMessage();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
AppTest appTest = new AppTest();
appTest.productionDelayMessage();
for (int i = 0; i < 10; i++) {
new Thread(new DelayMessage()).start();
cdl.countDown();
}
}
}
实现效果如下:

生产环境使用注意:
由于这种实现方式简单,但在生产环境下大多是多实例部署,所以存在并发问题,即缓存的查找和删除不具有原子性(zrangeWithScores和zrem操作不是一个命令,不具有原子性),会导致消息的多次发送问题,这个问题的避免方法如下:
1.可以采用单独一个实例部署解决(不具备高可用特性,容易单机出现故障后消息不能及时发送)
2.采用redis的lua脚本进行原子操作,即原子操作查找和删除(实现难度大)
因此,延时队列的实现最好采用rabbitMQ来实现,rabbitMQ天然具备分布式的特性,可以很好的用在多服务,多实例环境下,具体的实现参考https://my.oschina.net/u/3266761/blog/1926588
转载地址:https://my.oschina.net/u/3266761/blog/1930360
redis实现简单延时队列(转)的更多相关文章
- Redis简单延时队列
Redis实现简单延队列, 利用zset有序的数据结构, score设置为延时的时间戳. 实现思路: 1.使用命令 [zrangebyscore keyName socreMin socreMax] ...
- Redis实现简单消息队列
http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...
- 用redis实现简单的队列
在工作中,时常会有用到队列的场景,比较常见的用rabbitMQ这些专业的组件,官网地址是:http://www.rabbitmq.com,重要的是官方有.net的客户端,但是如果对rabbitMQ不熟 ...
- 你知道Redis可以实现延迟队列吗?
最近,又重新学习了下Redis,深深被Redis的魅力所折服,我才知道Redis不仅能快还能慢(我想也这么优秀o(╥﹏╥)o),简直是个利器呀. 咳咳咳,大家不要误会,本文很正经的啦! 好了,接下来回 ...
- 基于Redis实现延时队列服务
背景 在业务发展过程中,会出现一些需要延时处理的场景,比如: a.订单下单之后超过30分钟用户未支付,需要取消订单 b.订单一些评论,如果48h用户未对商家评论,系统会自动产生一条默认评论 c.点我达 ...
- 【转】基于Redis实现延时队列服务
背景 在业务发展过程中,会出现一些需要延时处理的场景,比如: a.订单下单之后超过30分钟用户未支付,需要取消订单b.订单一些评论,如果48h用户未对商家评论,系统会自动产生一条默认评论c.点我达订单 ...
- Redis学习笔记之延时队列
目录 一.业务场景 二.Redis延时队列 一.业务场景 所谓延时队列就是延时的消息队列,下面说一下一些业务场景比较好理解 1.1 实践场景 订单支付失败,每隔一段时间提醒用户 用户并发量的情况,可以 ...
- 了解一下Redis队列【缓兵之计-延时队列】
https://www.cnblogs.com/wt645631686/p/8454021.html 我们平时习惯于使用 Rabbitmq 和 Kafka 作为消息队列中间件,来给应用程序之间增加 异 ...
- Redis学习笔记02-消息队列与延时队列
写在前面:Redis的消息队列并不是专业的消息队列,没有ACK保证,没有特别多的高级特性,如果对消息的可靠性有很高的要求,就放弃它吧. 1.Redis消息队列 Redis通过内部的list数据结构来实 ...
随机推荐
- 生产环境手把手部署ERC20智能合约
工具 rimex http://remix.ethereum.org/ metamask https://metamask.io/ ERC20 代码 https://github.com/OpenZe ...
- BZOJ 1975: [Sdoi2010]魔法猪学院——K短路,A*
传送门 http://www.lydsy.com/JudgeOnline/problem.php?id=1975 题意&简要做法 一张有向图,求出最多的互不相同的路径,满足路径长度之和\(\l ...
- Selenium_Grid
Selenium Grid 工作原理 Grid是一种分布式测试工具,整个结果由一个hub主节点和若干个node代理节点组成. hub用来管理各个代理节点的注册和状态信息,并且接收远程客户端代码请求调用 ...
- SICP第三章题解
目录 SICP第三章题解 ex3-17 ex3-18 ex3-19 队列 ex3-21 ex3-22 ex3-24 ex3-25 3.4 并发:时间是一个本质问题 ex3-38 3.4.2 控制并发的 ...
- CSU 1424 Qz’s Maximum All One Square
原题链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1424 逐渐找到做这种题的感觉了. 二分法.g[i][j]存储坐标(i, j)的值,s[i ...
- 使用jdk自带的工具native2ascii 转换Unicode字符和汉字
1.控制台转换 1.1 将汉字转为Unicode: C:\Program Files\Java\jdk1.5.0_04\bin>native2ascii 测试 \u6d4b\u8bd5 1.2 ...
- 【PAT】1006. 换个格式输出整数 (15)
1006. 换个格式输出整数 (15) 让我们用字母B来表示“百”.字母S表示“十”,用“12...n”来表示个位数字n(<10),换个格式来输出任一个不超过3位的正整数.例如234应该被输出为 ...
- vars 变量预解析
JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting(悬置/置顶解析/预解析).当你使用了一个变量,然后不久在函 ...
- 洛谷 P4513 小白逛公园-区间最大子段和-分治+线段树区间合并(单点更新、区间查询)
P4513 小白逛公园 题目背景 小新经常陪小白去公园玩,也就是所谓的遛狗啦… 题目描述 在小新家附近有一条“公园路”,路的一边从南到北依次排着nn个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩 ...
- ref:浅谈XXE漏洞攻击与防御
ref:https://thief.one/2017/06/20/1/ 浅谈XXE漏洞攻击与防御 发表于 2017-06-20 | 分类于 web安全 | 热度 3189 ℃ 你会挽着我 ...