DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How To Build Self-Hosted RSS Feed Reader Using Spring Boot and Redis
  • Distributed Task Synchronization: Leveraging ShedLock in Spring
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)

Trending

  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Immutable Secrets Management: A Zero-Trust Approach to Sensitive Data in Containers
  • AI's Dilemma: When to Retrain and When to Unlearn?
  1. DZone
  2. Coding
  3. Frameworks
  4. Asynchronous Task Execution Using Redis and Spring Boot

Asynchronous Task Execution Using Redis and Spring Boot

In this article, we are going to learn how to use Spring Boot 2.x and Redis to execute asynchronous tasks.

By 
Sonu Kumar user avatar
Sonu Kumar
·
Updated May. 18, 21 · Tutorial
Likes (26)
Comment
Save
Tweet
Share
69.5K Views

Join the DZone community and get the full member experience.

Join For Free
Asynchronous tasksLearn more about executing asynchronous tasks using Redis and Spring Boot.

In this article, we are going to learn how to use Spring Boot 2.x and Redis to execute asynchronous tasks, with the final code demonstrating the steps described in this post.

You may also like: Spring and Threads: Async

Spring/Spring Boot

Spring is the most popular framework available for Java application development. As such, Spring has one of the largest open source communities. Besides that, Spring provides extensive and up-to-date documentation that covers the inner workings of the framework and sample projects on their blog — there are 100K+ questions on StackOverflow. 

In the early days, Spring only supported XML-based configuration, and, because of that, it was prone to many criticisms. Later, Spring introduced an annotation-based configuration that changed everything. Spring 3.0 was the first version that supported the annotation-based configuration. In 2014, Spring Boot 1.0 was released, completely changing how we look at the Spring framework ecosystem. A more detailed timeline can be found here. 

Redis

Redis is one of the most popular NoSQL in-memory databases. Redis supports different types of data structures. Redis supports different types of data structures, e.g. Sets, Hash tables, Lists, simple key-value pairs, just to name a few. The latency of Redis calls and operations are bot sub-milliseconds, which makes it even more attractive across the developer community.

Why Asynchronous Task Execution

A typical API call consists of five things:

  1. Execute one or more database (RDBMS/NoSQL) queries.
  2. One or more operations on some cache systems (In-Memory, Distributed, etc.).
  3. Some computations (it could be some data crunching doing some math operations).
  4. Calling some other service(s) (internal/external).
  5. Schedule one or more tasks to be executed at a later time or immediately, but in the background.

A task can be scheduled at a later time for many reasons. For example, an invoice must be generated seven days after the order creation or shipment. Similarly, email notifications do not need to be sent immediately, so we can delay them. 

With these real-world examples in mind, sometimes, we need to execute tasks asynchronously to reduce API response time. For example, we get a request to delete 1K+ records at once, if we delete all of these records in the same API call, then the API response time would be increased for sure. To reduce API response time, we can run a task in the background that would delete those records. 

Delayed Queue

Whenever we schedule a task to run at a given time or a certain interval, then we use cron jobs that are scheduled at a specific time or interval. We can run schedule tasks using different tools like UNIX style crontabs, Chronos; if we’re using Spring frameworks then we can use an out-of-the-box Scheduled annotation. 

Most cron jobs find the records for when a particular action has to be taken, e.g. finding all shipments after seven days have elapsed and for which invoices were not generated. Most of these scheduling mechanisms suffer scaling problems, where we do scan database(s) to find the relevant rows/records. In many situations, this leads to a full table scan which performs very poorly. Imagine the case where the same database is used by a real-time application and this batch processing system. As it's not scalable, we would need some scalable system that can execute tasks at a given time or interval without any performance problems. There are many ways to scale in this way, like running tasks in a batched fashion or operating tasks on a particular subset of users/regions. Another way could be to run a specific task at a given time without depending on other tasks, like serverless functions. A delayed queue can be used in cases where as soon as the timer reaches the scheduled time a job would be triggered. There’re many queuing systems/software available, but very few of them provide this feature, like SQS which provides a delay of 15 minutes, not an arbitrary delay like 7 hours or 7 days.

Rqueue

Rqueue is a message broker built for the Spring framework that stores data in Redis and provides a mechanism to execute a task at any arbitrary delay. Rqueue is backed by Redis since Redis has some advantages over other widely used queuing systems, like Kafka or SQS. In most web applications' backends, Redis is used to store either cached data or other purposes. In today's world, 8.4% of web applications are using the Redis database.

Generally, for a queue, we use either Kafka, SQS, or some other systems. These systems bring an additional overhead in different dimensions, e.g. money that can be reduced to zero using Rqueue and Redis.

Apart from the cost, if we use Kafka then we need to do infrastructure setup, maintenance, i.e. more ops, as most of the applications are already using Redis so we won’t have ops overhead. In fact, the same Redis server/cluster can be used with Rqueue, as Rqueue supports an arbitrary delay.

Message Delivery

Rqueue guarantees at-least-once message delivery, as long data is not lost in the database. You can read more about this here: Introducing Rqueue.

Tools we will need:

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

We're going to use Spring Boot for simplicity. We'll create a Gradle project from the Spring Boot initializer at https://start.spring.io/.

For dependencies, we will need:  

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

The directory/folder structure is shown below:

folder structure

We’re going to use the Rqueue library to execute any tasks with any arbitrary delay. Rqueue is a Spring-based asynchronous task executor that can execute tasks at any delay. It’s built with the Spring messaging library and backed by Redis.

We’ll add the Rqueue Spring Boot starter 2.7.0 dependency:

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

Java
 




x
13


 
1
dependencies {  
2
  implementation 'org.springframework.boot:spring-boot-starter-data-redis'
3
  implementation 'org.springframework.boot:spring-boot-starter-web'
4
  implementation 'com.github.sonus21:rqueue-spring-boot-starter:2.0.0-RELEASE'
5
  compileOnly 'org.projectlombok:lombok'   
6
  annotationProcessor 'org.projectlombok:lombok'
7
  providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
8
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
9
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'  
10
  }
11
}


For testing purposes, we will enable the Spring Web MVC feature so that we can send test requests. 

Update the application file as shown below:

Java
 




xxxxxxxxxx
1


 
1
@SpringBootApplication
2
@EnableRedisRepositories
3
@EnableWebMvc
4
public class AsynchronousTaskExecutorApplication { 
5
  public static void main(String[] args) { 
6
    SpringApplication.run(AsynchronousTaskExecutorApplication.class, args);
7
  }
8
}


Adding listeners using Rqueue is very simple. This is as simple as annotating a method with RqueueListener. The RqueuListener annotation has many fields that can be set based on the use case. For dead letter queues, set deadLetterQueue to push tasks to another queue, otherwise, the task will be discarded on failure. We can also set how many times a task should be retried using the numRetries field.

Create a Java file named MessageListener and add some methods to execute message in the background:

Java
 




xxxxxxxxxx
1
14


 
1
@Component
2
@Slf4j
3
public class MessageListener {
4

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

          
10
  @RqueueListener(value = "${invoice.queue.name}")
11
  public void generateInvoice(Invoice invoice) {
12
    log.info("Invoice {}", invoice);
13
  }
14
}


We will need Email and Invoice classes to store email and invoice data respectively. For simplicity, classes would only have a handful of fields.

Invoice.java:

Java
 




xxxxxxxxxx
1


 
1
import lombok.Data;
2

          
3
@Data
4
@AllArgsConstructor
5
@NoArgsConstructor
6
public class Invoice {
7
  private String id;
8
  private String type;
9
}


Email.java:

Java
 




xxxxxxxxxx
1
11


 
1
import lombok.Data;
2

          
3
@Data
4
@AllArgsConstructor
5
@NoArgsConstructor
6
public class Email {
7
  private String email;
8
  private String subject;
9
  private String content;
10
}


Task Submissions

A task can be submitted using the RqueueMessageEnqueuer bean. RqueueMessageEnqueuer has multiple methods to enqueue tasks depending on the use case use one of the available methods. For simple tasks use enqueue for delayed tasks use enqueueIn.

We need to auto-wire RqueueMessageEnqueuer or use the constructor to inject this bean.

Here's how to create a Controller for testing purposes. 

We're going to schedule invoice generation that can be done in 30 seconds. For this, we'll submit a task with 30000 (milliseconds) delay on the invoice queue. Also, we'll try to send an email that can be executed in the background. For this purpose, we'll add two GET methods, sendEmail and generateInvoice; we can use POST as well.

Java
 




xxxxxxxxxx
1
30


 
1
@RestController
2
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
3
@Slf4j
4
public class Controller {
5
  private @NonNull RqueueMessageEnqueuer rqueueMessageEnqueuer;
6

          
7
  @Value("${email.queue.name}")
8
  private String emailQueueName;
9

          
10
  @Value("${invoice.queue.name}")
11
  private String invoiceQueueName;
12

          
13
  @Value("${invoice.queue.delay}")
14
  private Long invoiceDelay;
15

          
16
  @GetMapping("email")
17
  public String sendEmail(
18
      @RequestParam String email, @RequestParam String subject, @RequestParam String content) {
19
    log.info("Sending email");
20
    rqueueMessageEnqueuer.enqueue(emailQueueName, new Email(email, subject, content));
21
    return "Please check your inbox!";
22
  }
23

          
24
  @GetMapping("invoice")
25
  public String generateInvoice(@RequestParam String id, @RequestParam String type) {
26
    log.info("Generate invoice");
27
    rqueueMessageEnqueuer.enqueueIn(invoiceQueueName, new Invoice(id, type), invoiceDelay);
28
    return "Invoice would be generated in " + invoiceDelay + " milliseconds";
29
  }
30
}


Add the following in the application.properties file:

Properties files
 




xxxxxxxxxx
1


 
1
email.queue.name=email-queue
2
invoice.queue.name=invoice-queue
3
# 30 seconds delay for invoice
4
invoice.queue.delay=300000


It's time to fire up the Spring Boot app. Once the application successfully starts, hit the below link to send an email.

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

In the log, we can see the email task is being executed in the background:

Image title

Below is our invoice scheduling after 30 seconds:

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

Image title

Conclusion

We can now schedule tasks using Rqueue without much boiler plate code! We made important considerations when configuring and using the Rqueue library. One important thing to keep in mind is that whether a task is a delayed task or not, by default, it's assumed that tasks need to be executed as soon as possible.

The complete code for this post can be found in my GitHub repo.  

If you found this post helpful, please share it with your friends and colleagues, and don't forget to give it a thumbs up!

Further Reading

Spring Boot: Creating Asynchronous Methods Using @Async Annotation

Spring and Threads: Async

Distributed Tasks Execution and Scheduling in Java, Powered by Redis

Spring Framework Task (computing) Spring Boot Redis (company) Database Execution (computing)

Published at DZone with permission of Sonu Kumar. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How To Build Self-Hosted RSS Feed Reader Using Spring Boot and Redis
  • Distributed Task Synchronization: Leveraging ShedLock in Spring
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!