使用Redis和Spring Boot的异步任务执行


在本文中,我们将学习如何使用Spring Boot 2.x和Redis执行异步任务,最后的代码演示了本文中描述的步骤。

您可能还会喜欢:Spring和线程:异步

Spring/Spring Boot

Spring是可用于Java应用程序开发的最流行的框架。因此,Spring拥有最大的开源社区之一。除此之外,Spring在其博客上提供了广泛而最新的文档,涵盖了框架的内部工作原理和示例项目-StackOverflow上有10万多个问题。

在早期,Spring仅支持基于XML的配置,因此,它很容易受到批评。后来,Spring引入了基于注释的配置,该配置更改了所有内容。Spring 3.0是第一个支持基于注释的配置的版本。2014年,发布了Spring Boot 1.0,彻底改变了我们对Spring框架生态系统的看法。可以在此处找到更详细的时间表。

Redis Redis是最流行的NoSQL内存数据库之一。Redis支持不同类型的数据结构。Redis支持不同类型的数据结构,例如集合,哈希表,列表,简单的键-值对,仅举几例。Redis调用和操作的延迟为bot亚毫秒级,这使其在开发人员社区中更具吸引力。

为什么要执行异步任务 一个典型的API调用包括五件事:

  1. 执行一个或多个数据库(RDBMS / NoSQL)查询。
  2. 在某些缓存系统(内存中,分布式等)上的一项或多项操作。
  3. 一些计算(可能是做一些数学运算的一些数据处理)。
  4. 调用其他服务(内部/外部)。
  5. 安排一个或多个任务在以后或立即在后台执行。

出于多种原因,可以在以后的某个时间安排任务。例如,必须在订单创建或装运后7天生成发票。同样,不需要立即发送电子邮件通知,因此我们可以延迟它们。

考虑到这些实际示例,有时,我们需要异步执行任务以减少API响应时间。例如,我们收到一个立即删除1K +记录的请求,如果我们在同一API调用中删除所有这些记录,那么肯定会增加API响应时间。为了减少API响应时间,我们可以在后台运行一个任务,以删除这些记录。

Delayed Queue 每当我们计划任务以给定时间或特定间隔运行时,我们就会使用计划在特定时间或间隔进行的cron作业。我们可以使用UNIX风格的crontabs,Chronos等其他工具来运行计划任务。如果我们使用的是Spring框架,则可以使用现成的Scheduled注释。

大多数cron作业会查找何时需要采取特定措施的记录,例如,在7天后查找所有发货以及未生成发票的记录。这些调度机制中的大多数都存在缩放问题,我们在其中扫描数据库以查找相关的行/记录。在许多情况下,这会导致全表扫描表现很差 想象一下实时应用程序和此批处理系统使用相同数据库的情况。由于它不可扩展,因此我们需要一些可扩展的系统,该系统可以在给定的时间或间隔执行任务,而不会出现任何性能问题。有许多以这种方式扩展的方法,例如以批处理方式运行任务或在用户/区域的特定子集上运行任务。另一种方法是在给定时间运行特定任务,而不依赖于其他任务,例如无服务器功能。一个延迟队列可以的情况下,一旦定时器达到预定时间的作业将被触发使用。有许多可用的排队系统/软件,但很少有提供此功能的系统,例如SQS 它提供15分钟的延迟,而不是7个小时或7天之类的任意延迟。

Rqueue Rqueue是为Spring框架构建的消息代理,该代理将数据存储在Redis中,并提供了一种在任意延迟下执行任务的机制。由于Redis与其他广泛使用的排队系统(例如Kafka或SQS)相比,具有一些优势,因此Rqueue得到了Redis的支持。在大多数Web应用程序的后端中,Redis用于存储缓存的数据或其他目的。在当今世界上,有8.4% 的Web应用程序正在使用Redis数据库。

通常,对于队列,我们​​使用Kafka,SQS或其他一些系统。这些系统带来了不同维度的额外开销,例如,使用Rqueue和Redis可以将金钱减少为零。

除了成本外,如果我们使用Kafka,那么我们需要进行基础架构设置,维护,即需要更多操作,因为大多数应用程序已经在使用Redis,因此我们不会有操作开销。实际上,相同的Redis服务器/群集可与Rqueue一起使用,因为 Rqueue支持任意延迟。

rqueue-broker.png

Message Delivery Rqueue保证至少一次发送消息,因为长时间的数据不会在数据库中丢失。您可以在这里阅读更多有关此内容:Rqueue简介。

我们将需要的工具:

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

为了简单起见,我们将使用 Spring Boot 。我们将从https://start.spring.io/的Spring Boot初始化程序创建一个Gradle项目。

对于依赖关系,我们将需要:

  1. Spring Data Redis
  2. Spring Web
  3. Lombok

目录/文件夹结构如下所示:

121024-pm.png

我们将使用Rqueue库以任意延迟执行任何任务。Rqueue是基于Spring的异步任务执行器,可以在任何延迟下执行任务。它是由Spring消息传递库构建的,并由Redis支持。

我们将添加Rqueue Spring Boot starter 2.7.0依赖项:

com.github.sonus21:rqueue-spring-boot-starter:2.7.0-RELEASE

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:2.0.0-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'  
  }
}

出于测试目的,我们将启用Spring Web MVC功能,以便我们可以发送测试请求。

如下所示更新应用程序文件:

@SpringBootApplication
@EnableRedisRepositories
@EnableWebMvc
public class AsynchronousTaskExecutorApplication { 
  public static void main(String[] args) { 
    SpringApplication.run(AsynchronousTaskExecutorApplication.class, args);
  }
}

使用Rqueue添加侦听器非常简单。这就像用注释方法一样简单RqueueListener。该RqueuListener注释具有可根据使用情况来设置多个领域。对于死信队列,设置deadLetterQueue为将任务推送到另一个队列,否则,任务将在失败时被丢弃。我们还可以使用该字段设置任务应重试多少次。 numRetries

创建一个名为的Java文件,MessageListener并添加一些方法以在后台执行消息:

@Component
@Slf4j
public class MessageListener {

  @RqueueListener(value = "${email.queue.name}")
  public void sendEmail(Email email) {
    log.info("Email {}", email);
  }

  @RqueueListener(value = "${invoice.queue.name}")
  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;
}

任务提交 可以使用RqueueMessageEnqueuer Bean提交任务。有多种方法可以使任务排队,具体取决于用例,请使用一种可用方法。对于简单任务,请使用延迟任务。 RqueueMessageEnqueuerenqueueenqueueIn

我们需要自动连线RqueueMessageEnqueuer或使用构造函数来注入此bean。

这是创建用于测试目的的Controller的方法。

我们将计划在30秒内完成发票生成。为此,我们将在发票队列中提交延迟为30000(毫秒)的任务。另外,我们将尝试发送可以在后台执行的电子邮件。为此,我们将添加两个GET方法,sendEmail和; 我们也可以使用POST。 generateInvoice

@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class Controller {
  private @NonNull RqueueMessageEnqueuer rqueueMessageEnqueuer;
  @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");
    rqueueMessageEnqueuer.enqueue(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");
    rqueueMessageEnqueuer.enqueueIn(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

现在该启动Spring Boot应用程序了。应用程序成功启动后,请点击以下链接以发送电子邮件。

http:// localhost:8080 / email?email=xample@exampl.com&subject=%22test%20email%22&content=%22testing%20email%22

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

g6a.png

Below is our invoice scheduling after 30 seconds:

http://localhost:8080/invoice?id=INV-1234&type=PROFORMA

invoice.png

结论 现在,我们可以使用Rqueue计划任务,而无需太多样板代码!在配置和使用Rqueue库时,我们进行了重要考虑。要记住的重要一件事是,默认情况下,无论任务是延迟任务还是非延迟任务,都假定需要尽快执行任务。


原文链接:https://codingdict.com/