redis(六)---- 简单延迟队列
延迟队列的应用场景也很常见,例如:session的超时过期、自动取消未付款订单等等。redis中有一种数据结构叫做zset,即有序集合。元素类型为String类型,且元素具有唯一性不能重复,每个元素可附带float类型的score即分值。从zset中获取元素的时候可以通过分值进行排序后获取某个分值范围内的元素或所有元素。
public class DelayQueue {
private String redisHost = "10.5.31.155";
private int redisPort = 6379;
private Jedis redis;
private String queueMapKey = "DelayQueueMap";
private String queueSetKey = "DelayQueueSet";
private int delaySecond = 3;
@Before
public void before() {
redis = new Jedis(redisHost, redisPort);
}
@Test
public void pub() throws InterruptedException {
for (int i = 1; i <= 100000; i++) {
String messageId = UUID.randomUUID().toString().replace("-", "");
String messageBody = "第" + i + "条消息:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
redis.hset(queueMapKey, messageId, messageBody);
redis.zadd(queueSetKey, System.currentTimeMillis() + (delaySecond * 1000), messageId);
Thread.sleep(Math.round(Math.floor(Math.random() * 2000)));
}
}
@Test
public void sub() throws InterruptedException {
while (true) {
Set<Tuple> messages = redis.zrangeByScoreWithScores(queueSetKey, System.currentTimeMillis() - (delaySecond * 1000), System.currentTimeMillis());
for (Tuple message : messages) {
Long zrem = redis.zrem(queueSetKey, message.getElement());
if (zrem > 0) {
String messageBody = redis.hget(queueMapKey, message.getElement());
redis.hdel(queueMapKey, message.getElement());
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) + ":" + messageBody);
}
}
Thread.sleep(1000);
}
}
@After
public void after() {
redis.close();
}
}
输出结果:可以看到pub的消息都是延迟3秒被消费的。
2018-09-28 15:02:58.863:第1条消息:2018-09-28 15:02:52.891
2018-09-28 15:02:58.866:第2条消息:2018-09-28 15:02:54.240
2018-09-28 15:02:59.870:第3条消息:2018-09-28 15:02:56.011
2018-09-28 15:02:59.872:第4条消息:2018-09-28 15:02:56.373
2018-09-28 15:02:59.874:第5条消息:2018-09-28 15:02:56.587
上面的代码模拟了一个简单的延迟队列,思路如下:
- 在redis中建立一个hash类型数据,用来存储消息id及消息内容
- 在redis中建立一个zset类型数据,用来存储消息id及对应的分数(该消息的过期时间)
- pub端推送消息时候,先写hash数据,再写zset数据。
- sub端定时按照分数从zset中获取消息id,获取到消息id后逐个删除,删除成功后再处理消息。
缺点:
- sub端需要定时轮训,所以会出现不及时消费的情况
- 如果pub端的生产能力大于sub端的消费能力,则会导致redis内数据越来越多
需要注意的是:
- sub端获取消息、删除消息、处理消息不是一个原子操作,在高并发的情况下,获取到的消息可能被其他服务删除。所以在删除、处理消息的时候,不能一次性删除所有获取到的消息,而是要逐条删除后判断是否删除成功再处理消息。以免消息被重复消费。
- 为什么要用消息id呢,不能直接把消息内容放在zset里吗?答案是不可以。消息内容是可能重复的,而zset中的String是不能重复的。
- 存在消息id及消息内容的数据不能直接使用redis的String数据结构吗?答案是不建议。因为这样会导致redis的key急剧曾多。
redis(六)---- 简单延迟队列的更多相关文章
- 灵感来袭,基于Redis的分布式延迟队列(续)
背景 上一篇(灵感来袭,基于Redis的分布式延迟队列)讲述了基于Java DelayQueue和Redis实现了分布式延迟队列,这种方案实现比较简单,应用于延迟小,消息量不大的场景是没问题的,毕竟J ...
- PHP基于Redis实现轻量级延迟队列
延迟队列,顾名思义它是一种带有延迟功能的消息队列. 那么,是在什么场景下我才需要这样的队列呢? 一.背景 先看看一下业务场景: 1.会员过期前3天发送召回通知 2.订单支付成功后,5分钟后检测下游环节 ...
- 灵感来袭,基于Redis的分布式延迟队列
延迟队列 延迟队列,也就是一定时间之后将消息体放入队列,然后消费者才能正常消费.比如1分钟之后发送短信,发送邮件,检测数据状态等. Redisson Delayed Queue 如果你项目中使用了re ...
- 你知道Redis可以实现延迟队列吗?
最近,又重新学习了下Redis,深深被Redis的魅力所折服,我才知道Redis不仅能快还能慢(我想也这么优秀o(╥﹏╥)o),简直是个利器呀. 咳咳咳,大家不要误会,本文很正经的啦! 好了,接下来回 ...
- 使用netty HashedWheelTimer构建简单延迟队列
背景 最近项目中有个业务,需要对用户新增任务到期后进行业务处理.使用定时任务定时扫描过期时间,浪费资源,且不实时.只能使用延时队列处理. DelayQueue 第一想到的是java自带的延时队列del ...
- Redis实现简单消息队列
http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...
- redis实现简单延时队列(转)
继之前用rabbitMQ实现延时队列,Redis由于其自身的Zset数据结构,也同样可以实现延时的操作 Zset本质就是Set结构上加了个排序的功能,除了添加数据value之外,还提供另一属性scor ...
- 用redis实现简单的队列
在工作中,时常会有用到队列的场景,比较常见的用rabbitMQ这些专业的组件,官网地址是:http://www.rabbitmq.com,重要的是官方有.net的客户端,但是如果对rabbitMQ不熟 ...
- Redis简单延时队列
Redis实现简单延队列, 利用zset有序的数据结构, score设置为延时的时间戳. 实现思路: 1.使用命令 [zrangebyscore keyName socreMin socreMax] ...
随机推荐
- Program-Language
1. 主流编程语言 2. 编程语言分类 2.1 编译or解释 2.2 按照客观系统的描述可分为两类 2.3 按照编程范型可分为 3. 语言范式 Paradigm 4. 计算机语 ...
- Linux-kernel-timeline
Linux kernel Protocol Location HTTP https://www.kernel.org/pub/ GIT https://git.kernel.org/ RSYNC rs ...
- Thinkcmf任意漏洞包含漏洞分析复现
简介 ThinkCMF是一款基于PHP+MYSQL开发的中文内容管理框架,底层采用ThinkPHP3.2.3构建.ThinkCMF提出灵活的应用机制,框架自身提供基础的管理功能,而开发者可以根据自身的 ...
- C# 绘制矩形方框读写内存类 cs1.6人物透视例子
封装的有问题 其中方框可能在别的方向可能 会显示不出来建议不要下载了 抽时间我会用纯c#写一个例子的 其中绘制方框文字和直线调用的外部dll采用DX11(不吃CUP)绘制我封装成了DLL命名为 S ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-forward
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- sqlite if not exists应用实例
INSERT or replace INTO [main].[RecordInfo]([WorkID],[bArtificialAttendance],[fThreshold],[Attendance ...
- 指令——mkdir
一个完整的指令的标准格式: Linux通用的格式——#指令主体(空格) [选项](空格) [操作对象] 一个指令可以包含多个选项,操作对象也可以是多个 指令mkdir——(make directory ...
- nginx location语法解释
1.没有修饰符 表示:必须以指定模式开始,如: 默认模式 server { server_name baidu.com; location /abc { …… } } htt ...
- ES6与ES5的继承
ES6 ES6中的类 类与ES5中的构造函数写法类似 区别在于属性要放入constructor中,静态方法和属性实列不会继承 <script> class Person{ height=& ...
- 利用 Python 破解 ZIP 或 RAR 文件密码
我们经常会从网络上下载一些带密码的压缩包,想要获取里面的内容,往往就要给提供商支付一些费用.想要白嫖其中的内容,常见的做法是百度搜索一些压缩包密码破解软件,但后果相信体验过的人都知道.本文将会利用 P ...