使用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调用包括五件事
-
执行一个或多个数据库(RDBMS/NoSQL)查询
-
在某些缓存系统(内存中、分布式等)上的一个或多个操作
-
一些计算(可能是一些数据处理,做一些数学运算)
-
调用其他服务(内部/外部)
-
将一个或多个任务安排在稍后时间或立即执行,但在背景
由于许多原因,可以在稍后时间安排任务。例如,发票必须在订单创建或装运后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等。
目录/文件夹结构如下所示:
我们要用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);
}
}
我们需要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
豆子。 它有多个方法来根据用例使用可用方法之一对任务进行排队。对于简单的任务,使用队列来处理延迟的任务。
我们需要自动连线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
现在,我们可以运行这个应用程序了。
在日志中,我们可以看到电子邮件任务正在后台执行:
以下是30秒后的发票时间安排:
结语
我们现在可以使用RQueue来调度任务,而不需要太多的代码!在配置和使用RQueue库时,我们考虑了一些重要问题。需要记住的一件重要事情是:在默认情况下,任务是否是延迟的任务,假设任务需要尽快执行。
如果你觉得这篇文章有帮助,请与你的朋友和同事分享,不要忘了竖起大拇指!
赞 0