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?
Related
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.
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.
The first controller is sending the success response after that it will call another method after sending the response.
I need to call m1()method after return the response
#RequestMapping(value = {"/hello"}, method = POST, produces = { "application/json" })
public Response getAllData(#RequestBody String request){
return new ResponseEntity<>("Hello World");
}
public void m1(){
}
The simple trick is to use try...finally.
try{
return new Response();
} finally {
//do after actions
}
'finally' will always execute after the try block no matter there is a return statement in it.
Example for the Spring AspectJ using #AfterReturning advice
#Aspect
#Component
public class A {
/*
* AfterReturning advice
*/
#AfterReturning("execution(* com.package.ClassName.getAllData(..))")
public void m1(JoinPoint joinPoint) {
}
}
You need to add #EnableAsync in your configuration class or main class
then create another service class encapsulating an Async method m1
In your class add the statement below:
asyncServiceImpl.m1();
#Service
public class AsyncServiceImpl {
#Async
public CompletableFuture<String> m1() {
// Your logic here
return CompletableFuture.completedFuture(String.valueOf(Boolean.FALSE));
}
}
you can use eventListener for creating event.
And catch it in public method with EventListener annotation.
https://www.baeldung.com/spring-events
The most simple and reliable way - run a thread. Try-final isn't good at all.
But the best solution is - throw out that SB and use pure JEE servlets to invoke all that you need (JSON, JPA, BM transactions) in the client's request's thread, so you will never get stuck like that.
I'm very new to multi-threading and CompletableFuture, and I wanted to know if it is possible to implement the following scenario in Spring container (Spring Boot 2) and #Async.
I have several services that basically construct a larger object, and I wanted to see if I can make the granular services run asynchronously.
I have a service (ServiceC) that depends on the output of another service (ServiceA), while the third one can run independently (ServiceB). someService is needed for all the other services, so I thought I don't need to make it an asynchronous call. Below is the layout:
#Service
public class ServiceA {
#Async
public CompletableFuture<A> doServiceAWork(int id) throws AppException {
return CompletableFuture.completedFuture(getA(id));
}
}
#Service
public class ServiceB {
#Async
public CompletableFuture<B> doServiceBWork(int id) {
return CompletableFuture.completedFuture(getB(id));
}
}
#Service
public class ServiceC {
#Async
public CompletableFuture<C> doServiceCWork(A a) {
return CompletableFuture.completedFuture(getC(a));
}
}
#Service
public class ServiceOrchestration {
// Necessary autorwiring of services
public BigObj constructBigObj(AppObj obj) throw SomeAppException {
int id = someService.getId(obj);
BigObj bigObj = new BigObj();
try {
// Conceptualizing here
CompletableFuture<A> cfA = serviceA.doServiceAWork(id);
CompletableFuture<B> cfB = serviceB.doServiceBWork(id);
// Obviously C depends on A
CompletableFuture<C> cfC = serviceB.doServiceCWork(cfA.get());
CompletableFuture.allOf(cfA, cfB, cfC).join();
bigObj.setA(cfA.get());
bigObj.setB(cfB.get());
bigObj.setC(cfC.get());
} catch (AppException | ExecutionException | InterruptedException e) {
throw new SomeAppException("..", e);
}
return BigObj;
}
}
My requirements are as following:
Invoke asynchronous call to ServiceC when ServiceA is done as the returned data should be fed to ServiceC in order to proceed
If a call to ServiceA throws an exception (AppException), it needs to be propagated to the caller -- in this case ServiceOrchestration#constructBigObject(), which handles it as above. In other word, if serviceA.doServiceAWork(id) throws an exception, every other asynchronous call should be canceled but the exception should be thrown in constructBigObj in order to be caught and handled
Wait for all the asynchronous calls to be completed then construct BigObj
For the second requirement, I looked at isCompletedExceptionally() but wasn't sure where it needs to be called from, e.g. before a call to join()? How do I get the root exception then?
How does the asynchronous chaining works here between ServiceA call and ServiceC where there are dependencies for calling one, at the same time, I need values from both, e.g. A and C, to construct BigObj?
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.