0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何使用Spring Boot 2.x和Redis执行异步任务?

冬至子 来源:ImportNew 作者:ImportNew 2023-06-06 15:57 次阅读

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 调用包括以下五个方面:

  1. 执行数据库(RDBMS 或 NoSQL)查询
  2. 在某些缓存系统(内存、分布式等)上执行操作
  3. 进行计算(对一些数据进行数学计算)
  4. 调用其他(内部或外部)服务
  5. 调度任务稍后执行或者在后台立即执行。

任务可根据需要定时执行,例如在创建订单或装运单后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 简介中可以了解更多信息

需要的工具:

  1. IDE
  2. Gradle
  3. Java
  4. Redis

简单起见,这里使用 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
@EnableWebMvc
public 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
@Slf4j
public 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
@NoArgsConstructor
public class Invoice {
  private String id;
  private String type;
}

Email.java:

import lombok.Data;
@Data
@AllArgsConstructor
@NoArgsConstructor
public 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))
@Slf4j
public 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-queue
invoice.queue.name=invoice-queue
# 30 seconds delay for invoice
invoice.queue.delay=300000

现在可以运行该程序。程序成功启动后,访问 链接。

在日志中,可以看到电子邮件任务正在后台执行:

图片

下面是延迟30秒生成发票:

图片

总结

可以看到,使用 Rqueue 执行定时任务不需要冗长的模板代码!配置和使用 Rqueue 库时,进行了仔细考虑。要记住:无论是普通任务还是延迟任务,都需要尽快执行。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    102994
  • XML技术
    +关注

    关注

    0

    文章

    15

    浏览量

    5990
  • RDBMS
    +关注

    关注

    0

    文章

    9

    浏览量

    5793
  • nosql
    +关注

    关注

    0

    文章

    38

    浏览量

    9921
  • Redis
    +关注

    关注

    0

    文章

    362

    浏览量

    10492
收藏 人收藏

    评论

    相关推荐

    Spring Boot如何实现异步任务

    Spring Boot 提供了多种方式来实现异步任务,这里介绍三种主要实现方式。 1、基于注解 @Async @Async 注解是 Spring
    的头像 发表于 09-30 10:32 487次阅读

    Spring Boot Starter需要些什么

    ,写一个pulsar-spring-boot-starter是非常有必要的,在此之前,我们先看看一个starter需要些什么。 Spring Boot Starter spring-boot
    的头像 发表于 09-25 11:35 460次阅读
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> Starter需要些什么

    基于maven的spring-data-redis整合

    springredis的整合
    发表于 04-12 14:03

    redis缓存注解怎么使用

    spring boot —— redis 缓存注解使用教程
    发表于 09-11 14:43

    Spring bootRedis的使用

    【本人秃顶程序员】springboot专辑:Spring bootRedis的使用
    发表于 03-27 11:42

    Spring Boot从零入门1 详述

    在开始学习Spring Boot之前,我之前从未接触过Spring相关的项目,Java基础还是几年前自学的,现在估计也忘得差不多了吧,写Spring
    的头像 发表于 12-10 22:18 417次阅读

    Spring认证」什么是Spring GraphQL?

    这个项目建立在 Boot 2.x 上,但它应该与最新的 Boot2.4.x5 相关。 要创建项目,请转到start.spring.io并为要使用的GraphQL传输选择启动器: 启动机
    的头像 发表于 08-10 14:08 609次阅读
    「<b class='flag-5'>Spring</b>认证」什么是<b class='flag-5'>Spring</b> GraphQL?

    如何实现一个秒杀系统

    实现一个秒杀系统,采用spring boot 2.x + mybatis+ redis + swagger2 + lombok实现。
    的头像 发表于 09-15 09:56 1732次阅读

    Spring Boot特有的实践

    Spring Boot是最流行的用于开发微服务的Java框架。在本文中,我将与你分享自2016年以来我在专业开发中使用Spring Boot所采用的最佳实践。这些内容是基于我的个人经验
    的头像 发表于 09-29 10:24 643次阅读

    强大的Spring Boot 3.0要来了

    和 Bugfix。 Spring Boot 3.0 的开发工作始于实验性的 Spring Native,旨在为 GraalVM 原生镜像提供支持。 在该版本中,开发者现在可以使用标准 Spri
    的头像 发表于 10-31 11:17 1174次阅读

    Spring Boot中整合两种定时任务的方法

    框架 Quartz ,Spring Boot 源自 Spring+SpringMVC ,因此天然具备这两个 Spring 中的定时任务实现策
    的头像 发表于 04-07 14:55 1241次阅读
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>中整合两种定时<b class='flag-5'>任务</b>的方法

    Spring Boot中如何使用定时任务

    本文介绍在 Spring Boot 中如何使用定时任务,使用非常简单,就不做过多说明了。
    的头像 发表于 04-12 10:56 779次阅读

    Spring Boot Actuator快速入门

    一下 Spring Boot Actuator ,学习如何在 Spring Boot 2.x 中使用、配置和扩展这个监控工具。
    的头像 发表于 10-09 17:11 340次阅读

    Spring Boot的启动原理

    可能很多初学者会比较困惑,Spring Boot 是如何做到将应用代码和所有的依赖打包成一个独立的 Jar 包,因为传统的 Java 项目打包成 Jar 包之后,需要通过 -classpath 属性
    的头像 发表于 10-13 11:44 369次阅读
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>的启动原理

    Spring Boot 的设计目标

    什么是Spring Boot Spring BootSpring 开源组织下的一个子项目,也是 S
    的头像 发表于 10-13 14:56 309次阅读
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 的设计目标