Spring boot send async tasks - java

I need to send the email/sms/events as a background async task inside spring boot rest.
My REST controller
#RestController
public class UserController {
#PostMapping(value = "/register")
public ResponseEntity<Object> registerUser(#RequestBody UserRequest userRequest){
// I will create the user
// I need to make the asyn call to background job to send email/sms/events
sendEvents(userId, type) // this shouldn't block the response.
// need to send immediate response
Response x = new Response();
x.setCode("success");
x.setMessage("success message");
return new ResponseEntity<>(x, HttpStatus.OK);
}
}
How can I make sendEvents without blocking the response (no need to get the return just a background task) ?
sendEvents- call the sms/email third part api or send events to kafka topic.
Thanks.

Sounds like a perfect use case for the Spring #Async annotation.
#Async
public void sendEvents() {
// this method is executed asynchronously, client code isn't blocked
}
Important: #Async works only on the public methods and can't be called from inside of a single class. If you put the sendEvents() method in the UserController class it will be executed synchronously (because the proxy mechanism is bypassed). Create a separate class to extract the asynchronous operation.
In order to enable the async processing in your Spring Boot application, you have to mark your main class with appropriate annotation.
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.run(args);
}
}
Alternatively, you can also place the #EnableAsync annotation on any #Configuration class. The result will be the same.

Related

Can I return an API response without waiting on other external API calls in Spring Boot?

Is this the correct way to use #Async in Spring Boot?
#Service
class someServiceImpl {
...
public someResponseDTO getUsers(int userId) {
// Do some logic
...
// Call external API with another service method from another service impl
anotherService.emailUserInTheBackground(userId);
return someResponseDTO;
}
...
}
#Service
public class AnotherService {
#Async
public void emailUserInTheBackground(int userId) {
// This might take a while...
...
}
}
Since emailUserInTheBackground() has #Async annotation and void return type, does it block the line return someResponseDTO at all?
All I wanted is to return the response to the caller without waiting because emailUserInTheBackground() takes too long to complete and isn't directly tied to the response object.
Yes that is the correct way to run a task in the background, you can mimick the thread blocking behavior by introducing a delay.
#SpringBootApplication
#EnableAsync
public class MyApplication {
public static void main(String[] arg) {
SpringApplication.run(MyApplication.class);
}
}
then you need to mark the emailUserInTheBackground method with #Async annotation.
#Service
class AnotherService {
#Async
public void emailUserInTheBackground(int userId) {
try {
TimeUnit.SECONDS.sleep(10);
System.out.println("Print from async: "+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now add one more logger after a method call, you'll see getUsers(...) call completing first in a different thread even though the emailService thread is blocked for 10 seconds.
anotherService.emailUserInTheBackground(userId);
System.out.println("Print from service: "+ Thread.currentThread().getName());
you can also use CompletableFuture to run a task in the background.
public someResponseDTO getUsers(int userId) {
// some other task
...
// Call external API with another service method from another service impl
CompletableFuture.runAsync(() -> anotherService.emailUserInTheBackground(userId))
return someResponseDTO;
}
The relevant behavior of #Async is documented in the Spring documentation:
You can provide the #Async annotation on a method so that invocation of that method occurs asynchronously. In other words, the caller returns immediately upon invocation, while the actual execution of the method occurs in a task that has been submitted to a Spring TaskExecutor.
In the case you're describing, since the emailUserInTheBackground method is annotated with #Async and Spring's asynchronous method execution capability is enabled, the emailUserInTheBackground method will return immediately, and the call will be processed in a separate thread. The someResponseDTO value will be be returned from the getUsers method while the emailUserInTheBackground method continues to be processed in the background.

Call Scheduler after method

I have an app, which connects to an API every day and fetches a data (schedulers run every 24h). I would like to add such a functionality:
after user registration call schedulers and force fetching data right now. Could you recommend the best approach in Spring?
#Component
public class GetMyFeeEstimateScheduler extends Scheduler {
#Scheduled(fixedDelay = DELAY)
#Transactional
public void fetchGetMyFeeEstimate() throws Exception {
fetchData();
}
#PostMapping("/signup")
public void signUp(#RequestBody SignUpRequest signUpRequest) {
// ...
// CALL_SCHEDULERS
}
I would prefer to expose the fetchData() as a public method in a service class and then call from the controller and from scheduler.
Another option is to call fetchGetMyFeeEstimate directly from the controller.

How to transfer data via reactor's subscriber context?

I'm a new for a project reactor, but i have task to send some information from classic spring rest controller to some service, which is interacts with different system. Whole project developed with project reactor.
Here is my rest controller:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
testService.saveHeader(headers.get("header"));
return service.getData();
}
And here is my service:
#Service
public class Service {
private Mono<String> monoHeader;
private InteractionService interactor;
public Mono<String> getData() {
return Mono.fromSupplier(() -> interactor.interact(monoHeader.block()));
}
public void saveHeader(String header) {
String key = "header";
monoHeader = Mono.just("")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + ctx.get(key)))
.subscriberContext(ctx -> ctx.put(key, header));
}
Is it acceptable solution?
Fisrt off, I don't think you need the Context here. It is useful to implicitly pass data to a Flux or a Mono that you don't create (eg. one that a database driver creates for you). But here you're in charge of creating the Mono<String>.
Does the service saveHeader really achieve something? The call seem transient in nature: you always immediately call the interactor with the last saved header. (there could be a side effect there where two parallel calls to your endpoint end up overwriting each other's headers).
If you really want to store the headers, you could add a list or map in your service, but the most logical path would be to add the header as a parameter of getData().
This eliminates monoHeader field and saveHeader method.
Then getData itself: you don't need to ever block() on a Mono if you aim at returning a Mono. Adding an input parameter would allow you to rewrite the method as:
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header));
}
Last but not least, blocking.
The interactor seems to be an external service or library that is not reactive in nature. If the operation involves some latency (which it probably does) or blocks for more than a few milliseconds, then it should run on a separate thread.
Mono.fromSupplier runs in whatever thread is subscribing to it. In this case, Spring WebFlux will subscribe to it, and it will run in the Netty eventloop thread. If you block that thread, it means no other request can be serviced in the whole application!
So you want to execute the interactor in a dedicated thread, which you can do by using subscribeOn(Schedulers.boundedElastic()).
All in all:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
return service.getData(headers.get("header"));
}
}
#Service
public class Service {
private InteractionService interactor;
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header))
.subscribeOn(Schedulers.boundedElastic());
}
}
How to transfer data via reactor's subscriber context?
Is it acceptable solution?
No.
Your code of saveHeader() method is an equivalent of simple
public void saveHeader(String header) {
monoHeader = Mono.just(header);
}
A subscriberContext is needed if you consume the value elsewhere - if the mono is constructed elsewhere. In your case (where you have all code before your eyes in the same method) just use the actual value.
BTW, there are many ways to implement your getData() method.
One is as suggested by Simon Baslé to get rid of a separate saveHeader() method.
One other way, if you have to keep your monoHeader field, could be
public Mono<String> getData() {
return monoHeader.publishOn(Schedulers.boundedElastic())
.map(header -> interactor.interact(header));
}

Can a service update a controller in Spring boot for long processes

this is a question more about communication between service and controller annotated classes in spring boot. I have a RestController class that exposes a POST mapping which calls a method in the Service class. Now this method may take a long time running; hence there is a need to send some kind of feedback to the controller.
Is there any mechanism which allows a service to call/update a method/variable in the controller?
one of the most simplest ways is passing some lamda function from the controller to the service and call it from the service like this
Controller Class
#RestController
public class controller {
#Autowired
Service service;
public void foo() {
service.foo(..parms, (message/*any params you want*/) -> {
// here the body that will receive the message from the service
System.out.print(message);
});
}
}
Service Class
public class Service {
// updateStatus here is the function you will send the update to the controller from
public void foo(...params, updateStatus) {
updateStatus("starting the process...");
// do some code
updateStatus("in progress...");
// do some code
updateStatus("completed");
}
}

Feign Call from asynchronous method

I have Spring event Publisher and listener where the requirement is as below:
Trigger the event from some method.
Listen the event from listener class
As soon as event is listened Call another micro-service
So currently i am using openfeign to call another micro-service.
This call works fine when the listener method runs Synchronously and it fails when listener method is marked as #Async.
Below is the sample code:
Fiegn Interface configuration:
#AuthorizedFeignClient(name = "another-microservice")
public interface CustomFeignInterface {
#PostMapping("api/call")
public ResponseEntity<String> callMethod(#RequestBody Foo foo);
}
Spring event Listener:
#Component
public class FooEventListener {
#Autowired
private CustomFeignInterface customFeignInt;
#Async
#EventListener
public void handleCustomEvent(Foo foo) {
try {
ResponseEntity<String> res = customFeignInt.callMethod(foo);
} catch (Exception e) {
e.printStackTrace();
}
}
}
As per above code Call to another micro-service works perfect when i remove #Async but fails when i add #Async to FooEventListener.handleCustomEvent(..) method.
Somewhere i found that Feign does not support Async calls:
https://github.com/OpenFeign/feign/issues/361
But still i need async behavior how can i achieve that?

Categories

Resources