spring/spring boot
spring 是最流行 java 应用程序开发框架。因此,spring 社区也是最大的开源社区之一。除此之外,spring 博客还提供了最新的开发文档,内容非常丰富。涵盖了框架的内部工作原理和示例项目,在stackoverflow上有10万多个问题。
spring 早期只支持基于xml的配置,为此饱受批评。后来 spring 引入了基于注解的配置,情况发生了根本改变。spring 3.0是第一个支持基于注解的配置的版本。2014年发布的 spring boot 1.0,彻底改变了人们对 spring 框架生态的看法。在这里可以找到更详细的时间表。
redis
redis 是最流行的 nosql 内存数据库之一,支持不同类型的数据结构,包括 set、哈希表、list、简单键值对等。redis 调用延迟为亚毫秒级,支持 replica set 等功能。redis 操作的延迟也是亚毫秒级,在开发者社区中更具吸引力。
为什么需要异步执行任务
一个典型的 api 调用包括以下五个方面:
执行数据库(rdbms 或 nosql)查询在某些缓存系统(内存、分布式等)上执行操作进行计算(对一些数据进行数学计算)调用其他(内部或外部)服务调度任务稍后执行或者在后台立即执行。任务可根据需要定时执行,例如在创建订单或装运单后7天后生成发票。同样,电子邮件通知无需立即发送,因此可以设为延期发送。
考虑到这些实际场景,有时候需要异步执行任务,减少 api 响应时间。例如,如果一次在同一个 api 调用中删除一千多条记录,那么肯定会增加 api 响应时间。为了减少 api 响应时间,可以运行一个后台任务删除这些记录。
延迟队列
当计划在指定时间或者按照设定间隔执行任务时,可以使用 corn job。有很多不同的工具可以执行定时任务,比如 unix 风格的 crontabs、chronos。如果用 spring 框架,那么可以用默认提供的 scheduled 注解。
大多数 cron job 会在需要执行特定操作时查找记录,例如查找所有已发货7天但未生成发票的记录。这些调度机制中大多数都会遇到扩展问题,在数据库中扫描查找相关行或者记录。多数情况会引发全表扫描,性能非常差。设想一下正在运行的应用程序与批处理系统使用相同的数据库。
由于不可扩展,因此需要一些可扩展系统,可以在指定时间或按照设定时间间隔执行任务,不会出现任何性能问题。有许多扩展的方法,例如用批处理方式执行任务,或者在用户、区域子集上执行任务。另一种方法是在指定时间执行任务,任务之间没有依赖,例如 serverless 函数。定时器达到预定时间后会立即触发执行作业,这时可以使用延迟队列。有很多队列系统或软件可供使用,但很少像 sqs 这样可以设置延迟15分钟,而不是延迟7个小时或者7天。
rqueue
rqueue 是针对 spring 框架构建的消息代理,它把数据存储到 redis 中并且提供了一种机制可以按任意延迟执行任务。rqueue 得到了 redis 支持。相比 kafka、sqs 等常见队列系统,redis 具有一些优势。在大多数 web 应用后端程序中,redis 用来缓存数据或者其他用途。在当今世界,8.4%的 web 应用程序正在使用 redis 数据库。
通常,使用 kafka、sqs 或者其他队列系统会带来不同程度的额外开销,而 rqueue 和 redis 可以将费用降为零。
除了使用 kafka 带来的开销,还需配置基础架构、进行维护等等。由于大多数程序已经使用了 redis,因此不需要其他操作开销。实际上,可以在同一个 redis 服务器或群集上使用 rqueue。rqueue支持任意长度延迟
消息传递
由于长数据不会在数据库中丢失,rqueue 能够确保至少发送一次消息。在 rqueue 简介中可以了解更多信息。
需要的工具:
idegradlejavaredis简单起见,这里使用 spring boot。通过spring boot初始化程序 创建一个 gradle 项目。
需要添加以下依赖:
1.spring data redis
2.spring web
3.lombok 等。
目录/文件夹结构如下所示:
使用rqueue开发库实现按任意延迟时间执行任务。rqueue 是一个基于 spring 的异步任务执行器,可以按照任意延迟执行任务,它基于 spring 消息库并由 redis 提供支持。
这里将使用**com.github.sonus21:rqueue-spring-boot-starter:1.2-release添加 rqueue spring boot starter **依赖。
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'com.github.sonus21:rqueue-spring-boot-starter:1.2-release' compileonly 'org.projectlombok:lombok' annotationprocessor 'org.projectlombok:lombok' providedruntime 'org.springframework.boot:spring-boot-starter-tomcat' testimplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' }}需要启用 redis spring boot 功能。出于测试目的,这里还将启用 web mvc。
application 文件更新如下:
@springbootapplication@enableredisrepositories@enablewebmvcpublic class asynchronoustaskexecutorapplication { public static void main(string[] args) { springapplication.run(asynchronoustaskexecutorapplication.class, args); }}使用 rqueue 添加任务非常简单,只要对方法添加 rqueuelistener 注解。rqueulistener 注解提供了一些字段,可根据使用场景设置。对于延迟任务,需要设置 delayedqueue=true 并且必须提供 value;否则注解将被忽略。value 是队列名称。设置 deadletterqueue 可以将任务推送到另一个队列。否则,执行失败时任务会被丢弃。还可以使用 numretries 字段设置任务重试次数。
创建名为 messagelistener 的 java 文件并增加一些方法执行任务:
@component@slf4jpublic class messagelistener { @rqueuelistener(value = ${email.queue.name}) (1) public void sendemail(email email) { log.info(email {}, email); } @rqueuelistener(delayedqueue = true, value = ${invoice.queue.name}) (2) public void generateinvoice(invoice invoice) { log.info(invoice {}, invoice); }}用 email 和 invoice 类分别存储电子邮件和发票数据。简单起见,这些类只包含少数几个字段。
invoice.java:
import lombok.data;@data@allargsconstructor@noargsconstructorpublic class invoice { private string id; private string type;}email.java:
import lombok.data;@data@allargsconstructor@noargsconstructorpublic class email { private string email; private string subject; private string content;}提交任务
可以使用 rqueuemessagesender bean 提交任务。根据使用场景,可以采用多种方式设置任务,例如,对于重试可以使用方法重试计数,对于延迟任务可以设置延迟。
需要对 rqueuemessagesender 进行 autowire 或使用构造函数注入 bean。
下面展示了如何为测试创建 controller。
这里会在30秒内生成发票。为此,在发票队列上提交一个延迟30000(毫秒)的任务。另外,这里会尝试在后台发送一封电子邮件。为此添加两个 get 方法,sendemail 和 generateinvoice,当然也可以使用 post。
@restcontroller@requiredargsconstructor(onconstructor = @__(@autowired))@slf4jpublic class controller { private @nonnull rqueuemessagesender rqueuemessagesender; @value(${email.queue.name}) private string emailqueuename; @value(${invoice.queue.name}) private string invoicequeuename; @value(${invoice.queue.delay}) private long invoicedelay; @getmapping(email) public string sendemail( @requestparam string email, @requestparam string subject, @requestparam string content) { log.info(sending email); rqueuemessagesender.put(emailqueuename, new email(email, subject, content)); return please check your inbox!; } @getmapping(invoice) public string generateinvoice(@requestparam string id, @requestparam string type) { log.info(generate invoice); rqueuemessagesender.put(invoicequeuename, new invoice(id, type), invoicedelay); return invoice would be generated in + invoicedelay + milliseconds; }}在 application.properties 加入以下内容:
email.queue.name=email-queueinvoice.queue.name=invoice-queue# 30 seconds delay for invoiceinvoice.queue.delay=300000现在可以运行该程序。程序成功启动后,访问 链接。
在日志中,可以看到电子邮件任务正在后台执行:
下面是延迟30秒生成发票:
总结
可以看到,使用 rqueue 执行定时任务不需要冗长的模板代码!配置和使用 rqueue 库时,进行了仔细考虑。要记住:无论是普通任务还是延迟任务,都需要尽快执行。
NRK3302语音识别芯片智能窗帘上的语音方案
割集法
怎样控制步进电机快慢_步进电机不用脉冲如何驱动
一文看懂选择车载导航GPS模块厂家的技巧?
儒卓力携手威世为客户提供面向未来的电力电子解决方案
如何使用Spring Boot 2.x和Redis执行异步任务?
魅族宣布搭载骁龙888的新机将在 2021年春季发布
SPI与I2C的异同及优缺点
线损是什么?怎样才能降低配电网的线损?
康瑞连接器--电子线的概念
使用NB-IoT模块的短信功能,有什么需要注意的事项
可以极大延长系统主电池寿命的能量收集电源IC
合理的布局是PCB设计成功的第一步
为什么要做躺式电芯
盘点未来银行网点里的黑科技
Marvell凭借以太网交换机找到了汽车行业的强势入场券
软启动器接线图文大全
如何将XboxOne无线控制器连接到树莓派安装
杰华特推出JW1565氮化镓合封芯片,满足65W快充应用
CPLD与FPGA的区别对比概述