028-86261949

当前位置:首页 > 学科资讯 > 使用Redis和SpringBoot执行异步任务

使用Redis和SpringBoot执行异步任务

2020/05/18 16:34 分类: 学科资讯 浏览:2

Spring/Spring Boot


Spring是Java应用程序开发中最流行的框架。因此,Spring拥有最大的开源社区之一。除此之外,Spring还提供了大量最新的文档,这些文档涵盖了框架的内部工作和博客中的示例项目--有100 K+的问题StackOverflow. 


在早期,Spring只支持基于XML的配置,正因为如此,它受到许多批评。后来,Spring引入了一种基于注释的配置,它改变了一切。Spring3.0是第一个支持基于注释的配置的版本。在2014年,Spring Boot1.0已经发布,完全改变了我们看待Spring框架生态系统的方式。可以找到更详细的时间表。


Redis


Redis是内存中最流行的NoSQL数据库之一。redis支持不同类型的数据结构。redis支持不同类型的数据结构,例如:SET、Hash表、List、简单的键值对等等。Redis调用的延迟时间是亚毫秒,对副本集的支持等等。Redis操作的延迟是亚毫秒,这使得它在整个开发人员社区中更具吸引力。

 

异步任务执行的原因

 

一个典型的api调用包括五件事

 

  1. 执行一个或多个数据库(RDBMS/NoSQL)查询

  2. 在某些缓存系统(内存中、分布式等)上的一个或多个操作

  3. 一些计算(可能是一些数据处理,做一些数学运算)

  4. 调用其他服务(内部/外部)

  5. 将一个或多个任务安排在稍后时间或立即执行,但在背景

 

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


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

 

延迟队列


每当我们安排一个任务在给定的时间或某个时间间隔运行时,我们就使用在特定时间或间隔调度的cron作业。我们可以使用UNIX样式的crontab之类的不同工具来运行调度任务,史考斯,如果我们使用Spring框架,那么它是现成的排定注释❤️。


大多数cron作业都能找到必须采取某一特定行动的记录,例如,在七天后找到所有货物,而且没有为这些货物生成发票。这些调度机制中的大多数都会受到影响。标度问题,我们将扫描数据库以查找相关的行/记录。在许多情况下,这会导致全表扫描表现很差。假设一个实时应用程序和这个批处理系统使用相同的数据库。由于它不是可伸缩的,所以我们需要一些可伸缩的系统,它可以在给定的时间或间隔内执行任务,而不会出现任何性能问题。有许多方法可以以这种方式进行扩展,比如以批处理方式运行任务,或者在特定的用户/区域子集上操作任务。另一种方法可以是在给定的时间运行特定的任务,而不依赖于其他任务,比如无服务器功能。一个延迟队列可以在这样的情况下使用,在这种情况下,一旦计时器到达预定时间,就会触发作业。有很多排队系统/软件可用,但很少有系统/软件提供此功能,如SQS它提供了15分钟的延迟,而不是像7小时或7天这样的任意延迟。

 

 

RQueue


RQueue是为Spring框架构建的MessageBroker,该框架将数据存储在Redis中,并提供了在任意延迟时执行任务的机制。RQueue是由Redis支持的,因为与广泛使用的队列系统(如Kafka、SQS)相比,Redis具有一些优势。在大多数Web应用程序后端中,Redis用于存储缓存数据或其他用途。在当今世界,8.4% Web应用程序正在使用Redis数据库。


通常,对于队列,我们使用Kafka/SQS或其他一些系统,这些系统在不同的维度带来额外的开销,例如金钱,使用RQueue和Redis可以将这些开销降到零。
除了成本之外,如果我们使用Kafka,那么我们需要进行基础设施的设置、维护,即更多的OPS,因为大多数应用程序已经在使用Redis,所以我们不会有OPS开销,实际上,RQueue可以使用相同的Redis服务器/集群。RQueue支持任意延迟


讯息传递


RQueue保证至少一次消息传递,因为数据库中的数据不会丢失。更多地读到引入RQueue


我们需要的工具:
1.任何IDE
2.梯度
3.Java
4.红色


我们要用弹簧靴为了简单。我们将从SpringBoot初始化器创建一个Gradle项目

对于依赖项,我们需要:  
1.SpringDataRedis 2.SpringWeb3.Lombok等。
目录/文件夹结构如下所示:

folder structure

我们要用RQueue库执行任意延迟的任何任务。RQueue是一个基于Spring的异步任务执行器,它可以在任何延迟时执行任务,它建立在Spring消息传递库的基础上,并得到Redis的支持。

我们将使用com.github.sonus21:rqueue-spring-boot-starter:2.0.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'  
 
  }
 
 
}

我们需要启用RedisSpringBoot功能。出于测试目的,我们还将启用WebMVC。

将应用程序文件更新为:

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

使用RQueue添加任务非常简单。我们需要用RqueueListener。这个RqueuListener注释有几个字段可以根据用例设置。集deadLetterQueue将任务推送到另一个队列。否则,任务将在失败时被丢弃。我们还可以使用 numRetries场。

创建一个名为MessageListener并添加一些方法来执行任务:

@Component
 
@Slf4j
 
public class MessageListener {
 
 
  @RqueueListener(value = "${email.queue.name}") (1)
 
  public void sendEmail(Email email) {
 
    log.info("Email {}", email);
 
  }
 
 
  @RqueueListener(value = "${invoice.queue.name}") (2)
 
  public void generateInvoice(Invoice invoice) {
 
    log.info("Invoice {}", invoice);
 
  }
 
}

 

我们需要EmailInvoice类分别存储电子邮件和发票数据。为了简单起见,类将只有少数几个字段。

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 豆子。 它有多个方法来根据用例使用可用方法之一对任务进行排队。对于简单的任务,使用队列来处理延迟的任务。

我们需要自动连线RqueueMessageSender或者使用基于构造函数的注入来注入这些bean。

这是如何为测试目的创建控制器。 

我们将安排发票的生成,可以在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.enqueu(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.enqueueIn(invoiceQueueName, new Invoice(id, type), invoiceDelay);
    return "Invoice would be generated in " + invoiceDelay + " milliseconds";
  }
}

在applicy.properties文件中添加以下内容:

email.queue.name=email-queue
 
invoice.queue.name=invoice-queue
 
# 30 seconds delay for invoice
 
invoice.queue.delay=300000

 

现在,我们可以运行这个应用程序了。

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

Image title

 

 

以下是30秒后的发票时间安排:

Image title

结语

我们现在可以使用RQueue来调度任务,而不需要太多的代码!在配置和使用RQueue库时,我们考虑了一些重要问题。需要记住的一件重要事情是:在默认情况下,任务是否是延迟的任务,假设任务需要尽快执行。

如果你觉得这篇文章有帮助,请与你的朋友和同事分享,不要忘了竖起大拇指!

 

 

 

 

#标签:Redis,SpringBoot,异步任务