延迟队列的应用场景也很常见,例如: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

上面的代码模拟了一个简单的延迟队列,思路如下:

  1. 在redis中建立一个hash类型数据,用来存储消息id及消息内容
  2. 在redis中建立一个zset类型数据,用来存储消息id及对应的分数(该消息的过期时间)
  3. pub端推送消息时候,先写hash数据,再写zset数据。
  4. sub端定时按照分数从zset中获取消息id,获取到消息id后逐个删除,删除成功后再处理消息。

缺点:

  1. sub端需要定时轮训,所以会出现不及时消费的情况
  2. 如果pub端的生产能力大于sub端的消费能力,则会导致redis内数据越来越多

需要注意的是:

  1. sub端获取消息、删除消息、处理消息不是一个原子操作,在高并发的情况下,获取到的消息可能被其他服务删除。所以在删除、处理消息的时候,不能一次性删除所有获取到的消息,而是要逐条删除后判断是否删除成功再处理消息。以免消息被重复消费。
  2. 为什么要用消息id呢,不能直接把消息内容放在zset里吗?答案是不可以。消息内容是可能重复的,而zset中的String是不能重复的。
  3. 存在消息id及消息内容的数据不能直接使用redis的String数据结构吗?答案是不建议。因为这样会导致redis的key急剧曾多。

redis(六)---- 简单延迟队列的更多相关文章

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

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

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

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

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

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

  4. 你知道Redis可以实现延迟队列吗?

    最近,又重新学习了下Redis,深深被Redis的魅力所折服,我才知道Redis不仅能快还能慢(我想也这么优秀o(╥﹏╥)o),简直是个利器呀. 咳咳咳,大家不要误会,本文很正经的啦! 好了,接下来回 ...

  5. 使用netty HashedWheelTimer构建简单延迟队列

    背景 最近项目中有个业务,需要对用户新增任务到期后进行业务处理.使用定时任务定时扫描过期时间,浪费资源,且不实时.只能使用延时队列处理. DelayQueue 第一想到的是java自带的延时队列del ...

  6. Redis实现简单消息队列

    http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...

  7. redis实现简单延时队列(转)

    继之前用rabbitMQ实现延时队列,Redis由于其自身的Zset数据结构,也同样可以实现延时的操作 Zset本质就是Set结构上加了个排序的功能,除了添加数据value之外,还提供另一属性scor ...

  8. 用redis实现简单的队列

    在工作中,时常会有用到队列的场景,比较常见的用rabbitMQ这些专业的组件,官网地址是:http://www.rabbitmq.com,重要的是官方有.net的客户端,但是如果对rabbitMQ不熟 ...

  9. Redis简单延时队列

    Redis实现简单延队列, 利用zset有序的数据结构, score设置为延时的时间戳. 实现思路: 1.使用命令 [zrangebyscore keyName socreMin socreMax] ...

随机推荐

  1. zabbix_get工具基础使用

    zabbix_get工具基础使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.zabbix_get工具概述 我们在使用zabbix server监控zabbix agent端 ...

  2. php导出合同模板到excel

    /** * [export_excel 合同导出] * [@param itemid:单号] * @return [type] */ public function export_excel() { ...

  3. HashMap之Hash碰撞源码解析

    转自:https://blog.csdn.net/luo_da/article/details/77507315 https://www.cnblogs.com/tongxuping/p/827619 ...

  4. 易语言加壳SDK(宏标记)

    转载 http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece76310508c31490797634b87834e29938448e435061 ...

  5. 利用ZXing.Net生成和识别二维码

    ZXing.Net:ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库. github:https://github.com/micjahn/ZXing.Net 直接将字符 ...

  6. OPENCV2.46 与2.4.10以上版本的区别

    本人系统环境: CPU:Intel(R)Core(TM) i3-4160 CPU @ 3.60GHz 内存:4G 接两路摄像机,一路海康,一路艾普视达.CPU占有率95%.发现opencv2.4.10 ...

  7. [HNOI2008] 玩具装箱 D2 T3 斜率优化DP

    Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1... ...

  8. Java的包装类

    一.概述 因为基本数据类型的变量身上没有任何的方法和属性,所以针对基本数据类型提供了对应的类形式--包装类. 利用这个类产生对象,调用对象身上的方法来操作这个数据. 二.分类 包装类分为以下几种: 基 ...

  9. 5分钟搞懂:JWT(Json Web Token)

    https://www.qikegu.com/easy-understanding/892 JWT 基于token的用户认证原理:让用户输入账号和密码,认证通过后获得一个token(令牌),在toke ...

  10. springboot - 映射 HTTP Response Status Codes 到自定义 JSP Error 页面

    1.总览 2.代码 1).pom.xml <dependencies> <dependency> <groupId>org.springframework.boot ...