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

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

mhr18 2024-12-07 21:54 19 浏览 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

相关推荐

Dubai&#39;s AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East&#39;s Silicon Gateway

AI-generatedimageAsianFin--Dubaiisrapidlytransformingitselffromadesertoilhubintoaglob...

OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs

TMTPOST--OpenAIisescalatingthepricewarinlargelanguagemodel(LLM)whileseekingpartnershi...

黄仁勋说AI Agent才是未来!但究竟有些啥影响?

,抓住风口(iOS用户请用电脑端打开小程序)本期要点:详解2025年大热点你好,我是王煜全,这里是王煜全要闻评论。最近,有个词被各个科技大佬反复提及——AIAgent,智能体。黄仁勋在CES展的发布...

商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署

1、本文属于mini商城系列文档的第0章,由于篇幅原因,这篇文章拆成了6部分,本文属于第5部分2、mini商城项目详细文档及代码见CSDN:https://blog.csdn.net/Eclipse_...

Python+Appium环境搭建与自动化教程

以下是保姆级教程,手把手教你搭建Python+Appium环境并实现简单的APP自动化测试:一、环境搭建(Windows系统)1.安装Python访问Python官网下载最新版(建议...

零配置入门:用VSCode写Java代码的正确姿

一、环境准备:安装JDK,让电脑“听懂”Java目标:安装Java开发工具包(JDK),配置环境变量下载JDKJava程序需要JDK(JavaDevelopmentKit)才能运行和编译。以下是两...

Mycat的搭建以及配置与启动(mycat2)

1、首先开启服务器相关端口firewall-cmd--permanent--add-port=9066/tcpfirewall-cmd--permanent--add-port=80...

kubernetes 部署mysql应用(k8s mysql部署)

这边仅用于测试环境,一般生产环境mysql不建议使用容器部署。这里假设安装mysql版本为mysql8.0.33一、创建MySQL配置(ConfigMap)#mysql-config.yaml...

Spring Data Jpa 介绍和详细入门案例搭建

1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...

量子点格棋上线!“天衍”邀您执子入局

你是否能在策略上战胜量子智能?这不仅是一场博弈更是一次量子智力的较量——量子点格棋正式上线!试试你能否赢下这场量子智局!游戏玩法详解一笔一画间的策略博弈游戏目标:封闭格子、争夺领地点格棋的基本目标是利...

美国将与阿联酋合作建立海外最大的人工智能数据中心

当地时间5月15日,美国白宫宣布与阿联酋合作建立人工智能数据中心园区,据称这是美国以外最大的人工智能园区。阿布扎比政府支持的阿联酋公司G42及多家美国公司将在阿布扎比合作建造容量为5GW的数据中心,占...

盘后股价大涨近8%!甲骨文的业绩及指引超预期?

近期,美股的AI概念股迎来了一波上升行情,微软(MSFT.US)频创新高,英伟达(NVDA.US)、台积电(TSM.US)、博通(AVGO.US)、甲骨文(ORCL.US)等多股亦出现显著上涨。而从基...

甲骨文预计新财年云基础设施营收将涨超70%,盘后一度涨8% | 财报见闻

甲骨文(Oracle)周三盘后公布财报显示,该公司第四财季业绩超预期,虽然云基建略微逊于预期,但管理层预计2026财年云基础设施营收预计将增长超过70%,同时资本支出继上年猛增三倍后,新财年将继续增至...

Springboot数据访问(整合MongoDB)

SpringBoot整合MongoDB基本概念MongoDB与我们之前熟知的关系型数据库(MySQL、Oracle)不同,MongoDB是一个文档数据库,它具有所需的可伸缩性和灵活性,以及所需的查询和...

Linux环境下,Jmeter压力测试的搭建及报错解决方法

概述  Jmeter最早是为了测试Tomcat的前身JServ的执行效率而诞生的。到目前为止,它的最新版本是5.3,其测试能力也不再仅仅只局限于对于Web服务器的测试,而是涵盖了数据库、JM...

取消回复欢迎 发表评论: