范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

如何实现延迟队列(JDKmysqlredisRabbit)

  何为延迟队列
  队列,即先进先出的数据结构,就和食堂打饭一样,排在最前面的先打饭,打完饭就走;延迟队列即队列中的元素相比以往多了一个属性特征:延迟。延迟队列中的每个元素都指定了延迟时间,表示该元素到达指定时间之后将出队进行处理。其实从上述定义来看,与其说是延迟队列,不如说它是一个以时间为权重的最小堆结构。
  那么延迟队列有什么用呢?我们生活中其实平时接触到很多可以使用延迟队列来解决的例子:订单超时30分钟未付款将自动关闭会议系统中,会议开始前10分钟,发送会议提醒夏天晚上时,我们经常会给空调设置指定时长的时间,到时空调自动关闭再比如微波炉、烤箱、等等
  可以发现延迟队列想要实现的功能其实就是一个定时任务调度的一种。延迟队列实现方式
  延迟队列实现的方式有很多种,具体采用哪种去实现,和我们的业务背景、业务诉求都息息相关,不同的实现方式都有其适用的应用场景,我这里将延迟队列分为两类:单机延迟队列和分布式延迟队列。1. 单机实现
  JDK 提供了DelayedQueue可以实现延迟队列的目的。其类图如下:
  可以看到DelayedQueue是一个阻塞队列,其队列中的元素必须实现Delayed接口:public interface Delayed extends Comparable {     long getDelay(TimeUnit unit); }
  其中getDelay返回代表该元素的一个在队列中可存在的时间,通过这种方式来实现元素的延迟弹出。接下来看订单超时30秒将自动关闭的实际例子:public class JDKDelayQueueTest {     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");      private static final DelayQueue DELAY_QUEUE = new DelayQueue<>();      private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(2);      public static void main(String[] args) throws Exception {          EXECUTOR_SERVICE.submit(() -> {             while (true) {                 if (!DELAY_QUEUE.isEmpty()) {                     Order order = DELAY_QUEUE.poll();                     if (order != null) {                         System.out.println(order.getOrderId() + " 超时关闭与:" + FORMATTER.format(LocalDateTime.now()));                     }                  }                 TimeUnit.MILLISECONDS.sleep(1000);             }         });         EXECUTOR_SERVICE.submit(() -> {             try {                 DELAY_QUEUE.add(new Order("黄焖鸡订单"));                 TimeUnit.SECONDS.sleep(5);                 DELAY_QUEUE.add(new Order("麻辣香锅订单"));                 TimeUnit.SECONDS.sleep(10);                 DELAY_QUEUE.add(new Order("石锅拌饭订单"));             } catch (Exception e) {              }          });      }      public static class Order implements Delayed {          private final LocalDateTime expireTime;         private final String orderId;          public Order(String orderId) {             this.expireTime = LocalDateTime.now().plusSeconds(30);             this.orderId = orderId;             System.out.println(orderId + " 创建于:" + FORMATTER.format(LocalDateTime.now()));         }          @Override         public long getDelay(TimeUnit unit) {             return LocalDateTime.now().isAfter(expireTime) ? -1 : 1;         }          @Override         public int compareTo(Delayed targetOrder) {             // 谁的过期时间最早谁就排最前面             return this.expireTime.isBefore(((Order) targetOrder).getExpireTime()) ? -1 : 1;         }          public String getOrderId() {             return orderId;         }          public LocalDateTime getExpireTime() {             return expireTime;         }     } }
  输出:黄焖鸡订单 创建于:2021-08-21 18:26:30 麻辣香锅订单 创建于:2021-08-21 18:26:35 石锅拌饭订单 创建于:2021-08-21 18:26:45 黄焖鸡订单 超时关闭与:2021-08-21 18:27:00 麻辣香锅订单 超时关闭与:2021-08-21 18:27:05 石锅拌饭订单 超时关闭与:2021-08-21 18:27:15DelayQueue实现方式小结
  这种方式的优点就是实现简单,不复杂,但是其缺点也比较多:不具备可扩展性,内存限制、无持久化机制,数据容易丢失。
  分布式实现2. 数据库轮询
  数据库论询的方式相对而言也比较好理解,后台启动定时任务每隔一段时间扫描指定的数据库表每一行数据,获取出到达指定延迟时间的行进行处理,所以使用该方式重要的就三个要素:
  1)捞取任务
  扫描数据库的后台任务,可以使用分布式任务去扫,比如A任务扫描limit 0,100满足条件的数据行,B任务扫描limit 100,200满足条件的数据行
  2)执行任务
  一般来说讲究分工协作,第一步中的分布式线程任务专门用来捞取任务,那么捞取到的任务可以再次扔给另外专门用户处理任务的线程中
  3)数据库表设计
  可以在表中增加一个字段来表示延迟时间,比如针对上面的订单超时30秒关闭,我们可以增加一个字段timeout,可以是此时间的毫秒数来记录订单的超时时间,那么此时我们的SQL就可以是:select * from order where ${now} >= timeout limit ${start},100;
  数据库轮询实现方式小结
  采用这种方式可以看到首先我们需要查询数据库,那么查询数据库就意味着存在查询耗时,那么可能最终导致的就是实时性不高,但是它的优点在于天生满足任务持久化机制,不用担心延迟任务丢失。3.通过Redis实现
  Redis的五大数据类型中的zset数据类型中,包含一个称为score的属性,该数据类型中所有元素都会按照score进行排序,所以如果将score作为我们的延迟时间的时间戳,那么我们可以通过命令Zrangebyscore来获取满足条件的数据,然后交给我们的任务处理线程去处理,其实整个实现思想和数据库轮循是一样的,只不过数据存储结构由数据库转变成了redis,准确来说redis也是数据库,只不过不同的存储结构带来的影响就是适用场景的不同罢了。
  那么如果通过Redis来实现延迟队列,大概会有如下几步:
  1) 增加任务zadd tasks ${过期时间戳} ${任务相关数据}
  2)捞取任务ZRANGEBYSCORE tasks -inf ${当前时间戳} WITHSCORES
  捞取过期时间早于当前时间的这部分任务
  3)执行任务 接下来就是执行,这个就没什么好说的了
  关于redis zset数据结构以及命令可以看这里:https://www.runoob.com/redis/redis-sorted-sets.html一些优化点
  1.在添加延迟任务时,可以通过对任务id进行hash分散至多个redis key,可以避免所有任务存储在一个key中导致大key从而影响元素的添加和查找性能
  2.每个key独自拥有一个线程处理
  3.每个key的线程只负责拉取需要处理的数据,然后再转发至消息队列中,不做任何其他处理,可以提升处理速度,消息消费者可扩展性好,性能不够,机器来凑redis实现方式小结
  redis因为其都是内存中操作,所以查询插入速度和mysql来比都是非常快的,所以实时性会比mysql高,虽然redis也能满足任务数据的持久化,但是无法保证任务不丢失,所以这里持久性会比mysql稍弱一点
  4.使用消息队列
  我们可以采用rabbitMQ的延时队列。RabbitMQ具有以下两个特性,可以实现延迟队列RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letterlRabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能.Time-To-Live Extensions
  RabbitMQ允许我们为消息或者队列设置TTL(time to live),也就是过期时间。TTL表明了一条消息可在队列中存活的最大时间,单位为毫秒。也就是说,当某条消息被设置了TTL或者当某条消息进入了设置了TTL的队列时,这条消息会在经过TTL秒后"死亡",成为Dead Letter。如果既配置了消息的TTL,又配置了队列的TTL,那么较小的那个值会被取用。更多资料请查阅官方文档。Dead Letter Exchange
  刚才提到了,被设置了TTL的消息在过期后会成为Dead Letter。其实在RabbitMQ中,一共有三种消息的"死亡"形式:消息被拒绝。通过调用basic.reject或者basic.nack并且设置的requeue参数为false。消息因为设置了TTL而过期。消息进入了一条已经达到最大长度的队列。
  如果队列设置了Dead Letter Exchange(DLX),那么这些Dead Letter就会被重新publish到Dead Letter Exchange,通过Dead Letter Exchange路由到其他队列。不同实现方式的对比
  实现方式
  复杂度
  数据量
  持久化,数据丢失
  扩展性
  实时性
  jdk DelayQueue
  简单
  由于程序内存限制,适用于少数据量
  无持久化
  差
  高
  mysql 轮询
  稍微复杂
  可支持大数据量
  可保证持久化,保证任务不丢失
  可扩展
  由于查询开销,稍弱
  redis zet
  稍微复杂
  可支持大数据量
  可尽量保证持久化,不保证任务不丢失
  可扩展
  高
  RabbitMQ
  稍微复杂
  可支持大数据量
  可保证持久化,保证任务不丢失
  可扩展
  高结语
  除了以上实现方式,还有其他比如通过Rabbit MQ的TTL和死信队列来实现:每一个消息带有TTL属性,该TTL即延迟任务的延迟时间,只要超过指定时间没被消费,此消息将被转至死信队列中,我们可以监听死信队列消费消息进而达到延迟任务的目的;还有时间轮转算法等,时间有限,日后再学,日后再讲。

猫妹妹退出辛巴团队的原因?男朋友达少结婚了,新娘不是猫妹妹辛选巴伽娱乐的电商主播达少黄文达结婚领证了,新娘不是猫妹妹!猫妹妹说她和男朋友达少,两人分开的原因是,她想结婚,要一个家,可达少希望干事业挣钱。猫妹妹曾经退出辛巴团队的原因,还是因央视开播!又一部年代大剧来袭,四位国家一级演员作配,值得期待电视剧市场的竞争从来不会停止,为了将热度拿捏在自己手中,各大平台可谓是各出奇招,桃又拿出一部刑侦题材大剧回响。冯小刚导演亲手操刀,宋佳王阳等人实力加盟,剧情以女刑警视角去探寻一起凶山东籍十大女星,你认为哪位更漂亮?本文列举了8090年代出生的山东籍且为大众熟知的十大女演员,哪一位是你喜欢的呢,请留言评论吧!张雨绮张雨绮,1987年8月8日,出生于山东省德州市,毕业于上海戏剧学院附属戏曲学校,steam春季特卖!!干嘛那么贵??40以内新史低佳作速览Steam春季大特卖特刊第二弹来袭!这次我们为您精选了6款超值佳作,全部跌破40元!赶快来看看吧1通神榜15。12元通神榜是一款动作冒险休闲独立游戏,它的独特之处在于,你可以在游戏迷你枪战精英是抄袭来的吗?不是吧,迷你又跑去抄袭了,他还是通奸,那个秘密没有一丝丝改变,这次的迷你枪战精英也是一样的尿性,丝毫不再掩饰,光明正大的就搞起了抄袭,而这位被迫加入迷你抄袭名单的新选手就是使命召唤我在这个仙侠游戏里边劝退了很多人不用肝,又不用氪金,就想成为大哥有点不现实但是也不是,不可能。我最近玩的这个,感觉还可以,虽然不能跟榜一大哥相比,但是进游戏送的福利挺多的,每天都送。虽然不是特别多,但是也够用了。资深李信,分享点李信的干货经验(很干很干)我是玩了几千场李信的老玩家,最高省5,1万2战力。年限皮肤正好出了,李信的皮肤好评也是比较多的,作为老玩家,分享一些干货经验。这些经验是我玩过这么多以后总结下来好用的,也是我目前水毛晓彤毛晓彤以清纯的气质给人一种特别闪亮又毫无心机的感觉,那无辜的眼神瞬间就能拉近与人的距离,显出亲近感,而笑起来弯弯的眼睛更显甜美感,带来一种邻家妹妹的俏皮感。毛晓彤吸引我的地方是她利花朵元素再次流行,适合你,让你如花般美的花朵是怎样的呢?今天穿什么花朵元素,其实每隔五年或者十年,都会再次流行,今年花朵元素,再次流行起来了,花朵元素,是一个挺女人的元素,这个元素,你选到了适合你的,也搭配得当的话,真的可以让你美如花。分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照分享图片,貌美如花的陈数写真照素颜无人识的6位女星,带妆个个貌美如花,卸妆大姐你是谁都说娱乐圈的明星们颜值很高,她们荧屏上颜值就不用多说了,光鲜漂亮让人羡慕,和普通人站在一起更是对比特别的明显。这除了她们本身底子比较好以外,就是还有精致的妆容这道防线的原因。实际上
成都市的麻将馆里面,天天打麻将的人,他们的经济来源靠什么?成都人喜爱麻将是出了名的,要不怎么说在飞机上只要听到麻将声,就是到了成都呢?(笑)成都人每天打麻将一般分为两场下午13001800是首场,晚上20002400是夜场。大多数成都人一不干建筑行业了,最容易转行的工作是什么?我没从事过建筑行业,但这个行业的苦,我多少有些了解。我几个月前招进来的运营,之前从事的就是建筑行业。面试的时候我说,你要是过来了,薪资可不会比你之前高。他之前年薪15W。他说,我有五险一金有一年时间没交,后来异地开始缴纳,分别有什么影响?感谢邀请,更感谢楼主的提问。楼主您好,自己的五险一金有一年多的时间,没有正常的参保交费,后来异地开始交费了,分别会有什么影响呢?如果说你五险一金造成一年多没有参保交费,那么也就意味听说独生子女的父母退休时,单位都会一次性发3000元奖励吗?我是下岗职工,13年退休的,财政确实给了独生子女费3000元,在居委会填的表。每年居委会都会通知,原来是A4纸打印的贴在小区门口,现在会在小区群里通知。我爸妈十几年前退休后确实各种无锡市高级教师一年收入有多少?江苏无锡30年,高中高级,打卡10000,公绩金3200,绩效加其他大概每年5万。江苏无锡,中学高级,打卡12000,绩效奖平均8万。地标江苏无锡,年龄37岁,公办初中,学历硕士,从北上广深回武汉光谷的年轻人,现在过得怎么样?几个月前从深圳回到武汉光谷,看到这个标题就来答一下经济工资肯定是降了的,我回来找工作工资是降了几千,而以前在深圳的同事继续深圳跳槽工资是涨了几千,差别还是非常大的。生活以前在深圳,灵活再就业女性,自己交社保,退休年龄是50岁还是55岁?灵活就业人员,女性,如果是城镇户口,缴纳的城镇养老保险金,自己缴纳,交满当地最低年限。不含视同缴费年限,如果有视同缴费年限,还必须根据当地政策,实际缴纳养老保险金最低年限,视同缴费实体店生意越来越差,中年人的出路在哪里?我们经常听说,有的人天生就是做生意的料!人家说的是有的人,也就是一部分人,可是现在大家都错误的以为他也是有的人,盲目跟风,都以为钱是好赚的!事实上,很多人并不会做生意!回到问题,很你听过最真实的毛骨悚然的故事是什么?一个孩子的恶意能有多大?真。细思极恐故事分享于一位小学老师,主人公是3个12岁左右的孩子。暂且用ABC代称这三个孩子。一天,A去办公室找老师。A对老师说老师,我书包里的10块钱丢了快三十岁,在老家每月工资才三千多,还该不该坚持留在老家工作?在没有好的项目之前,不要因为冲动而去盲目投资创业。现在的经济形势不好,实体店生意也很难做。如果投资失败,让你辛苦多少年一夜可以回到解放前。打工起码有个稳定的收入,让你衣食无忧。如果刑满释放人员出狱当天是有监狱工作人员护送回家吗?有无儿无女无亲,无家可归年龄七八十岁刑满释放人员,会提前联系当地街道居委会或者是村委会安排好,监狱工作人员是会派人护送到当地指定居住地的。你是不是还没睡醒?我只听说押送去刑场吃花生