Table of Contents
What have we learned so far,
Part 1 – Spring Rest Advance : Input / Bean Validations
In this post, we discussed the steps to validate the input bean in Spring Rest.
Part 2 – Spring Rest Advance : Rest Documentation with Swagger 2
In this post, we discussed the steps to configure swagger to do the documentation for Spring Rest services
Part 3 – Spring Rest Advance : Spring Boot with H2 DB
In this post, we discussed the steps to configure and use H2 db in spring boot
Part 4 – Spring Rest Advance : Spring Rest Versioning
In this post we discussed the different ways to do the Rest versioning.
How To Do @Async in Spring Service
Context
Consider a situation when you need to create a resource or update a resource and the operation takes long time to complete.
Actually, this scenario is not that uncommon: after all, REST is not about manipulation of a couple of database rows in some CRUD scenarios but REST is about manipulation of arbitrary resources, and a resource might require extensive computation in order to come to existence.
So, you basically have two options:
you will force API client to wait until the resource is actually created
you can immediately return some status response, and defer creation to some later point
Why waiting is not cool in Spring
Well, because!
More seriously, there’s nothing wrong with forcing API client to wait. If on server side you rely on some code which takes more time to complete then your server will be busy for all the request to complete the execution of this code block and send the response which will eventually degrade the performance of your server and will take more time for the client to respond.
It is not unusual that your web service needs to communicate with another web service do some heavy operation in order to serve its clients. In the old days, that would imply that an incoming request to your server would capture one servlet connection, and perform a blocking call to the remote service before it can send a response to the client. It works, but it does not scale very well if you have multiple concurrent clients.
EmployeeSerice
EmailService – Lets add thread sleep of 5 seconds to simulate the sendMail takes sometime.
We are ready with our code. Lets run it and fire the HTTP POST with below data.
Url : http://localhost:8088/employees
data : { “firstName”: “only”, “lastName”: “fullstack”, “salary”: 1000, “email” : “saurabh@e.com” }
Output in console
Entered in addEmployee with : EmployeeDTO(id=null, firstName=only, lastName=fullstack, salary=1000, email=saurabh@e.com)
Employee saved into database
Entered in sendMail with mailId : saurabh@e.com
Wating for 5 seconds
Wating for 4 seconds
Wating for 3 seconds
Wating for 2 seconds
Wating for 1 seconds
Exited from sendMail
Exited from addEmployee
Here you can see that the client waits till the mail is sent to the user. Instead we can send the response to the client after the employee is inserted into the database and process the mail sending functionality asynchronously. Lets implement this in Spring Boot.
How to use @Async and @EnableAsync in Spring Boot
1. Mark the sendEmail method as @Async to execute it asynchronously.
What is the need of Asynchronous?
We can send the immediate response to the client and defer the execution later on the server. In this approach we speed up the server and client interaction and executes the remaining processing in an asynchronous way where a new thread is created to handle the execution. With Asynchronous code we defer the execution part in another thread and close the incoming HTTP rest connection by responding back. Let see how we can achieve this type of processing in Spring Rest. Scenario Suppose we have a resource called /employees which creates the employee on HTTP POST call. The creation of employee resource is time consuming as it requires to perform below two steps : 1. validate and save employee object into database 2. send an email to that employee In these two operation the second operation of sending an email takes time. Lets write code to understand it more clarly : Lets create a EmployeeControllerpackage com.onlyfullstack.springasyncexample.controller; import com.onlyfullstack.springasyncexample.datatransferobject.EmployeeDTO; import com.onlyfullstack.springasyncexample.domainobject.EmployeeDO; import com.onlyfullstack.springasyncexample.exception.EntityNotFoundException; import com.onlyfullstack.springasyncexample.service.EmailService; import com.onlyfullstack.springasyncexample.service.EmployeeService; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; import java.util.List; import java.util.stream.Collectors; @RestController @RequestMapping("employees") public class EmployeeController { @Autowired private EmployeeService service; @Autowired private EmailService emailService; @Autowired private ModelMapper mapper; @PostMapping public ResponseEntity<Object> addEmployee(@Valid @RequestBody EmployeeDTO employeeDTO) { System.out.println("Entered in addEmployee with : "+employeeDTO); EmployeeDO employeeDO = mapper.map(employeeDTO, EmployeeDO.class); service.addEmployee(employeeDO); System.out.println("Employee saved into database"); emailService.sendMail(employeeDO.getEmail()); System.out.println("Exited from addEmployee"); return new ResponseEntity<>(HttpStatus.CREATED); } }
package com.onlyfullstack.springasyncexample.service; import com.onlyfullstack.springasyncexample.domainobject.EmployeeDO; import com.onlyfullstack.springasyncexample.exception.EntityNotFoundException; import com.onlyfullstack.springasyncexample.repositoy.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class EmployeeServiceImpl implements EmployeeService{ @Autowired private EmployeeRepository repository; @Override public void addEmployee(EmployeeDO employee) { repository.save(employee); } }
package com.onlyfullstack.springasyncexample.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class EmailService { public void sendMail(String email) { System.out.println("Entered in sendMail with mailId : " + email); try { int seconds = 5; for (int i = 1; i <= 5; i++) { System.out.println("Wating for "+seconds+" seconds" ); Thread.sleep(1000); seconds --; } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Exited from sendMail"); } }
Simply put – annotating a method of a bean with @Async will make it execute in a separate thread i.e. the caller will not wait for the completion of the called method.
Lets run the application with these changes and fire the same request again.
Output:
Entered in addEmployee with : EmployeeDTO(id=null, firstName=only, lastName=fullstack, salary=1000, email=saurabh@e.com)
Hibernate: call next value for employee_id_seq
Hibernate: call next value for employee_id_seq
Hibernate: insert into employee_data (email, first_name, last_name, salary, id) values (?, ?, ?, ?, ?)
Employee saved into database
Exited from addEmployee —> here the response is sent to the client
Entered in sendMail with mailId : saurabh@e.com
Wating for 5 seconds
Wating for 4 seconds
Wating for 3 seconds
Wating for 2 seconds
Wating for 1 seconds
Exited from sendMail
As you can see, server has sent the 201 HTTP response code to the client after the employee has been inserted into the database and started the sendEmail functionality in new thread wherein the client was not waiting for that method to get complete.
@Service public class EmailService { @Async public void sendMail(String email) { System.out.println("Entered in sendMail with mailId : " + email); try { int seconds = 5; for (int i = 1; i <= 5; i++) { System.out.println("Wating for " + seconds + " seconds"); Thread.sleep(1000); seconds--; } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Exited from sendMail"); } }
2. To enable asynchronous implementation add @EnableAsync to your configuration file as below.
@SpringBootApplication @EnableAsync public class SpringAsyncExampleApplication { public static void main(String[] args) { SpringApplication.run(SpringAsyncExampleApplication.class, args); } @Bean public ModelMapper getModelMapper() { return new ModelMapper(); } }
So what does the @Async and @EnableAsync do in the background?
@EnableAsync enables Spring’s asynchronous method execution capability. By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named “taskExecutor” otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations.@Async will create a new thread and submit in the ThreadPool.