百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

延时队列我在项目里是怎么实现的?

yuyutoo 2024-10-12 01:23 3 浏览 0 评论

我是3y,一年CRUD经验用十年的markdown程序员?常年被誉为职业八股文选手

前阵子,有个小伙伴找到问我,如果要实现延时发送,那是基于什么来做的。

我看到这个问题之后,稍微思考了下,觉得确实也是austin平台所需要实现的功能。对于前端而言,只要让业务方在创建模板的时候填选屏蔽类型,后端根据这个字段增添一点点细节,这个需求就做完了,简单

延迟消息如何实现?

延迟消息就是字面上的意思:当接收到消息之后,我需要隔一段时间进行处理(相对于立马处理,它隔了一段时间,所以他叫延迟消息)。

在原生的Java有DelayQueue供我们去使用,在使用的时候,我们add进去的队列的元素需要实现Delayed接口(同时该接口继承了Comparable接口,所以我们DelayQueue有序的)

public interface Delayed extends Comparable<Delayed> {
    long getDelay(TimeUnit unit);
}

poll的源码上可以清晰地发现本质上就是在取数的时候判断了下时间

long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
  return q.poll();

有的人就反驳到:这不是废话吗?肯定要判断时间啊,不判断时间怎么知道我要延迟的消息什么时候执行

明白了这点之后,我们再来别的方案。因为在生产环境中是不太可能使用JDK原生延迟队列的,它是没有持久化的,重启就会导致数据丢失。

austin项目使用内存队列去解耦处理数据已经有人提出服务器重启的时候该怎么办,我的解决思路就是通过优雅关闭服务器这种手段去尽量避免数据丢失,而延迟队列这种就不能这么干了,我们等不了这么久的。

稍微想想还有什么存储适合当队列且有持久化机制的呢?

答案显而易见:Redis和消息队列(Kafka/RocketMQ/RabbmitMQ等)

我们先来看Redis里提供了一种数据结构叫做zset,它是可排序的集合并且Redis原生就支持持久化。有赞的延迟队列就是基于通过zset进行设计和存储的。整体架构如下图:

简单理解这张图就是:将需要延迟的消息放置Redis,通过Timer轮询得到可执行的消息,将可执行的消息放置不同的Topic供业务方自行消费。

更多的设计思路可以参考有赞的技术原文,这里我不再赘述:https://tech.youzan.com/queuing_delay/

通过timer去轮询zset查看是否有可执行的消息是一种思路,也有人通过Redis的过期回调的姿势也能达到延迟消息的效果(把消息执行的时间定义为key过期的时间,当key触发了过期回调,那说明该消息可执行了)。

说完Redis,我们再来看看消息队列。在austin项目上使用消息队列是Kafka,而Kafka在官方是没有提供延迟队列这种机制的。不过RabbmitMQRocketMQ都有对应的机制,我们可以简单看看窥探下它们的实现思路。

RabbmitMQ它的延迟队列机制本质上也是通过TTL(Time To Live 消息存活的时间)所实现的,当队列里的元素触发了过期时,会被送往到Dead Letter Exchanges(死信队列中)。我们可以将死信队列的元素再次转发,对其进行消费,从而达到延迟队列的效果。

毕竟RabbmitMQ是专门做消息队列的,所以它对消息的可靠性会比Redis更加高(消息投递的可靠性、至少处理一次的消费语义)

RocketMQ支持在我们投递消息的时候设置延迟等级

Message message = new Message("TestTopic", ("Hello scheduled message " + i).getBytes());
// This message will be delivered to consumer 10 seconds later.
message.setDelayTimeLevel(3);
// Send the message
producer.send(message);

默认支持18个延迟等级,分别是:

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

当我们设置了延迟等级的消息之后,RocketMQ不会把消息直接投递到对应的topic,而是转发到对应延迟等级的队列中。在Broker内部会为每个延迟队列起TimerTask来进行判断是否有消息到达了时间。

ScheduleMessageService#start

for (Map.Entry<Integer, Long> entry : this.delayLevelTable.entrySet()) {
    this.deliverExecutorService.schedule(new DeliverDelayedMessageTimerTask(level, offset), FIRST_DELAY_TIME, TimeUnit.MILLISECONDS);
}  

如果到期了,则将消息重新存储到CommitLog,转发到真正目标的topic

RocketMQ延迟队列比较感兴趣的,推荐看这篇文章:https://cloud.tencent.com/developer/article/1581368

实现需求

在前面提到我们可以利用JDK原生的延时队列,又或是Redis的zset数据结构或者其过期时间机制、又或是RabbitMQ使用TTL+死信队列机制、又或是RocketMQ的延时等级队列机制来实现我们的需求(延时队列)

针对此次需求,上面所讲的延时队列,我都没用到...

austin项目引入的是Kafka,不太可能去为了延时队列去引入第二种消息队列(RabbitMQ在互联网应该用得相对较少,RocketMQ需要改动配置文件的延迟等级才能支持更丰富的延时需求)。

如果基于Kafka或者Redis去二次开发延时队列,开发成本还是有不少的,在GitHub也还没捞到我想要的轮子。

于是,我换了一种方案:万物皆扫表

针对这次需求(晚上发的消息,次日早上发送),就不需要上延时队列,因为austin已经接入了分布式定时任务框架了(对应的实现是xxl-job

只要把晚上的接收到的消息扔进Redis list,然后启个定时任务(每天早上9点)轮询该list是否有数据,如果有再重新做处理就完事了。

总结

这篇文章主要讲述了如果我们要使用延时队列,我们可以有什么方案,他们的设计是怎么样的。在需求侧上看,这个需求就是「延时队列」的场景,但基于现状的系统架构和开发成本考虑,我们是可以用另类(分布式定时任务框架)的方式去把需求给实现了。

很多时候,我们看到的系统很烂,技术栈很烂,发现好多场景都没有用到最佳实践而感到懊恼,在年轻的时候都想有重构的心。但实际上每引入一个中间件都是需要付出成本的,粗糙也有粗糙的好处。

只要业务能完美支持,那就是好的方案。想要搞自己想搞的技术,那就做开源,如果有一天我觉得分布式定时任务来实现此次需求不顺眼了,我再花时间来重构才干掉,现在就这么实现吧( // TODO)。

如果你实在是觉得看着糟心,欢迎提个pull request,这样我就不得不把这种实现给干掉了(我对提过来的pull request都会谨慎且用心处理)

都看到这里了,点个赞一点都不过分吧?我是3y,下期见。

来源:https://www.cnblogs.com/Java3y/p/16346958.html

相关推荐

建筑福利-pdf转dwg格式转换器,再也不用描图-极客青年

作为一名经常熬夜画图的建筑狗或者cad用户,你体验过pdf图纸描图到cad吗?前几天一个老同学找我,说他的毕业设计需要我帮忙,发给我一份pdf图纸文件,问我怎么把pdf图纸转换成dwg格式。机智的我灵...

想学 HTML,不知从何入手?看完这篇文章你就知道了

很多人都说HTML是一门很简单的语言,看看书,看看视频就能读懂。但是,如果你完全没有接触过,就想通过看一遍教程,背背标签,想要完全了解HTML,真的有点太天真了。HTML中文...

「前端」HTML之结构

今天继续为大家分享前端的知识,如果对前端比较感兴趣的小伙伴,可以关注我,我会更大家继续分享更多与前端相关的内容,当然如果内容中又不当的或者文字错误的,欢迎大家在评论区留言,我会及时修改纠正。1.初识H...

手把手教你使用Python网络爬虫下载一本小说(附源码)

大家好,我是Python进阶者。前言前几天【磐奚鸟】大佬在群里分享了一个抓取小说的代码,感觉还是蛮不错的,这里分享给大家学习。...

用于处理pdf文件格式的转换器

在上传过程中如果单个文件太大则容易中断,而且文件太大的话对与存储也有些弊端。那么我们应该想到将文件进行压缩(注意这里压缩指的是不改变文件格式的压缩,而不是用变成压缩文件。这里就将以下用专门的软件压缩P...

乐书:在线 Kindle 电子书制作和转换工具

之前Kindle伴侣曾推荐过可以在Windows和Mac系统平台上运行的kindle电子书制作软件Sigil(教程),用它可以制作出高质量的的ePub格式电子书,当然最后还需要通...

付费文档怎么下载?教你5种方法,任意下载全网资源

网上查资料的时候,经常遇到需要注册登录或者付费的才能复制或者是下载,遇到这种情况大多数人都会选择重新查。...

捡来的知识!3种方法随便复制网页内容,白嫖真香呀

网上的资源真的多,所以许多人常常会从网上找资料。我们看到感兴趣的内容,第一时间可能会想要收入囊中。比如说截个图啊,或者挑选有意思的句子复制粘贴,记录下来。可是,有些时候,却会遇到这样的情况:1、内容不...

AI的使用,生成HTML网页。

利用deepseek,豆包,kimi以及通义千问,写入相同的需求。【写一个网页,实现抽奖功能,点击“开始”,按键显示“停止”,姓名开始显示在屏幕上,人员包括:“张三”,“里斯”,“Bool”,“流水废...

pdf转换成jpg转换器 4.1 官方正式版

pdf转换成jpg工具软件简介pdf转换成jpg转换器是一款界面简洁,操作方便的pdf转换成jpg转换器。pdf转换成jpg转换器可以将PDF文档转换为JPG,BMP,GIF,PNG,TIF图片文件。...

办公必备的office转换成pdf转换器怎么用?

2016-02-2415:53:37南方报道网评论(我要点评)字体刚从校园走出社会,对于快节奏的办公环境,难免会觉得有些吃力。在起步阶段力求将手头上的事情按时完工不出错,但是渐渐的你会发现,别人只...

为什么PDF转Word大多要收费?

PDF转Word大多都要收费?并非主要是因为技术上的难度,而是基于多方面的商业和版权考虑的,下面给大家浅分析下原因:...

如何用python生成简单的html report报告

前提:用python写了一个简单的log分析,主要也就是查询一些key,value出来,后面也可以根据需求增加。查询出来后,为了好看,搞个html表格来显示。需要的组件:jinja2flask...

学用系列|如何搞定word批量替换修改和格式转换?这里一站搞定

想必不少朋友都会碰到批量修改word文档内容、压缩文档图片、文件格式转换等重复性文档处理工作的需要,今天胖胖老师就推荐给大家一个免费工具XCLWinKits,一站搞定你所有的需要。什么是XCLWinK...

这款PDF文档转换神器,能帮你解决PDF使用中的许多难点

不管是平时的学习还是工作,相信许多朋友都经常接触PDF文件。可以说,PDF文件在我们的日常办公学习过程中的重要性和Word文档一样重要。在之前的更新中,小编介绍了几款非常不错的PDF文档格式转换软件,...

取消回复欢迎 发表评论: