Asynchronous Task Execution Using Redis and Spring Boot

In this article, we'll take a look at how to use Spring Boot 2.x and Redis to perform asynchronous tasks, and the complete code will demonstrate the steps in this post.





Spring / Spring Boot

Spring  is the most popular framework for developing Java applications. As such, Spring has one of the largest open source communities. In addition, Spring provides extensive and up-to-date documentation that covers the inner workings of the framework and sample projects on its blog, and there are over 100,000 questions and answerson  StackOverflow





In the beginning, Spring only supported XML-based configuration and was subject to a lot of criticism because of this. Spring later introduced annotation-based configuration that changed everything. Spring 3.0 was the first version to support annotation-based configuration. In 2014, Spring Boot  1.0 was released  , completely changing the way we look at the Spring framework ecosystem. A more detailed description of the history of Spring can be found  here





Redis

Redis is one of the most popular in-memory NoSQL databases. Redis supports different types of data structures. Redis supports various types of data structures such as Set, Hash table, List, simple key-value pairs to name but a few. Redis call latency is less than milliseconds, replica set support, and so on. Redis operation latency is less than milliseconds, making it even more attractive to the developer community.





Why Asynchronous Task Execution





A typical API call has five steps:





  1. Executing one or more database queries (RDBMS / NoSQL)





  2. One or more caching system operations (In-Memory, Distributed, etc.)





  3. Some calculations (this may be data processing while performing some mathematical operations)





  4. Calling other services (internal / external)





  5. ,    





. , - 7 .  , . 





, , API. , 1K , API, API .  API, , . 





, , cron, .  , , crontab UNIX, Chronos, Spring,    Scheduled ❤️. 





cron , , , , .    , , /.    ,  .  , .  , - , - .  , , /.  —  , .    , .  / , , SQS, , 15 , , ​​ 7 7 . .





Rqueue

Rqueue — ,  Spring,  Redis . Rqueue Redis, Redis , Kafka, SQS.  - Redis .   8,4% - Redis.





, Kafka/SQS, , , , , Rqueue Redis.





, Kafka, , , , Redis, , / Redis Rqueue. Rqueue





Rqueue , .    Rqueue.





, :





  1. IDE





  2. Gradle 





  3. Java





  4. Redis 





 Spring Boot .  Gradle Spring Boot  https://start.spring.io/.





:  





  1. Spring Data Redis





  2. Spring Web





  3. Lombok





/ :





  Rqueue  . Rqueue — Spring , , Spring Redis.





spring boot starter  Rqueue 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'  
  }
}
      
      







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



, .  deadLetterQueue



.  .  , ,  . numRetries







Java  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



 bean-. , .  enqueue, enqueueIn.





 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";
  }
}
      
      



application.properties :





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



.   .





, :





30 :





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





Now we can schedule tasks using Rqueue without a lot of additional code! The basic considerations for setting up and using the Rqueue library have been provided. One important thing to keep in mind is that regardless of whether the task is a pending task or not, the default is to assume that tasks need to be completed as soon as possible.





The complete code of this post can be found in the repository  on  GitHub





Additional reading

Spring Boot: Creating Asynchronous Methods Using @Async Annotation





Spring and Threads: Async





Distributed Tasks Execution and Scheduling in Java, Powered by Redis
























































All Articles