How to schedule task and get the response asynchronously? - java

I don't know if I've used the correct title, but I will try to explain the problem below.
I have the following method to check the payment status from external service:
#Autowired
PaymentService paymentService;
public void checkPaymentStatus(Payment p){
// sending
String status = paymentService.getPaymentStatus(p.getId());
}
It works in main thread and there might be hundreds of requests per second, so I decided to use CompletableFuture to run the tasks asynchronously in separate threads.
#Autowired
PaymentService paymentService;
public void checkPaymentStatus(Payment p){
// sending
CompletableFuture<Response> status = paymentService.getPaymentStatus(p.getId());
}
PaymentService.class
#Service
public class PaymentService{
private final RestTemplate restTemplate;
#Async
public CompletableFuture<Response> getPaymentStatus(long id){
Response results = restTemplate.getForObject(url, Response.class);
return CompletableFuture.completedFuture(results);
}
}
Configuration
#Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("payment-service-");
executor.initialize();
return executor;
}
It works perfectly, but I have another task here. Every request must be wait 30 seconds before sending to the external service.
How to solve this problem?
Update:
Update:
In this methods, I might use Thread sleep, but it is not the correct solution as it blocks the Thread for 30 seconds and next task might run after 60 secs, etc.
#Async
public CompletableFuture<Response> getPaymentStatus(long id){
// I might use Thread sleep here, but it is not the correct solution as it blocks the Thread for 30 seconds and next task might run after 60 secs, etc.
Response results = restTemplate.getForObject(url, Response.class);
return CompletableFuture.completedFuture(results);
}

Related

Spring Boot start/stop tasks dynamically

I have a Spring Boot application, I need to expose 2 endpoints to start and stop scheduled task. The cron/fixed delay expression is defined in database.This configuration is per client.For example if client A calls the start API, I need to start a scheduled task based on the config defined in the database for that client A. The logic inside the task is same for all the clients.
My question is, any no of clients can call start and stop APIs, So how to start the task dynamically when start api is called and also stop the task (only that particular client's task not all ) when the client calls the stop API
`
#RestController
class SchedulerController {
#Autowired
TaskScheduler taskScheduler;
ScheduledFuture<?> scheduledFuture;
#RequestMapping("start")
ResponseEntity<Void> start(#RequestParam String clientId) {
String fixedDelay = "get it from database for the provided client";
scheduledFuture = taskScheduler.schedule(excuteLogic(clientId), fixedDelay);
// generate a task id and save in db against clientid
return new ResponseEntity<Void>(HttpStatus.OK);
}
#RequestMapping("stop")
ResponseEntity<Void> stop(#RequestParam String clientId) {
// get the taskid from database for the clientId
// stop the task
return new ResponseEntity<Void>(HttpStatus.OK);
}
private Runnable excuteLogic(String clientId) {
return () -> {
// logic goes here
};
}
}
Planning to generate a task id and save it in the database against clientid ,but not sure how to stop particular task scheduled for the provided client.
For your use case, since you have a fixed delay, I would save myself the headache and just keep that task running always and depending on a simple flag either return from the worker or continue. So something along the lines of:
#RequestMapping("start")
ResponseEntity<Void> start(#RequestParam String clientId) {
//either start the task on app startup or start it here if not started
myFlagService.enableScheduler(clientId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
#RequestMapping("stop")
ResponseEntity<Void> stop(#RequestParam String clientId) {
myFlagService.disableScheduler(clientId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
//the scheduled task
private void myWorkerFunction() {
if (!myFlagService.isSchedulerEnabled(clientId)) {
return;
}
// do work
}

How can I invoke a new #Async method but wait a condition?

I have a Spring Boot app that implements an AMQP MessageListener. This listener invoke to an #Async method managed by ThreadPoolTaskExecutor with pool size. The problem occurs when there are many incoming messages, so these messages are lost because there are no asynchronous workers available.
I am using Spring Core 5.0.7-RELEASE, Java 8
This is my code:
AsyncConfigurator:
#EnableAsync
#Configuration
public class AsyncConfiguration extends AsyncConfigurerSupport {
#Override
#Bean("docThreadPoolTaskExecutor")
public Executor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("DocWorkerThread-");
executor.initialize();
return executor;
}
My Back Service (MyAsyncService):
#Async("docThreadPoolTaskExecutor")
#Override
public void generaDocumento(String idUser, int year) {
//... some heavy and slow process
}
My message listener:
...
#Autowired
private MyAsyncService myAsyncService;
#Override
#EntryPoint
public void onMessage(Message message) {
try {
final String mensaje = new String(message.getBody(), StandardCharsets.UTF_8);
final MyPojo payload = JsonUtils.readFromJson(mensaje , MyPojo.class);
myAsyncService.generaDocumento(payload.getIdUser(), Integer.valueOf(payload.getYear()));
} catch ( Throwable t ) {
throw new AmqpRejectAndDontRequeueException( t );
}
}
I need someone to give me some idea to solve this.
ThreadPoolTaskExecutor has a queue by default. Messages should be added to the queue when you reach the corePoolSize. When the queue is full then more workers will be started until you reach the maxPoolSize. You can check the current queue size by executor.getThreadPoolExecutor().getQueue().size() to verify if the messages are really lost or just stuck in the queue.

Interrupt HTTP request after a given number of seconds

Im using Java 1.8, dropwizard 1.3.5, and swagger inflection 1.0.13 for my API.
I have a method which takes an HTTP Request, delays 20 seconds, then returns a 200 status code response:
public ResponseContext delayBy20Seconds(RequestContext context) {
ResponseContext response = new ResponseContext().contentType(MediaType.APPLICATION_JSON_TYPE);
Thread.sleep(20000);
response.status(Response.Status.OK);
return response;
}
Say I want to return a 400 status code if the operation (which in this case takes 20 seconds), takes more than 15 seconds. How would I achieve this?
One way to do it without additional libraries is by using the java.util.concurrent package. The surest way to cancel a long-running task like this is by running it in a separate thread.
import java.util.concurrent.*;
...
private ExecutorService exec = Executors.newSingleThreadExecutor();
public ResponseContext delayBy20Seconds(RequestContext context) {
Callable<ResponseContext> task = new Callable<ResponseContext>() {
#Override
public ResponseContext call() throws Exception {
Thread.sleep(20000);
return new ResponseContext().contentType(MediaType.APPLICATION_JSON_TYPE);
}
};
List<Callable<ResponseContext>> tasks = new ArrayList<>();
tasks.add(task);
List<Future<ResponseContext>> done = exec.invokeAll(tasks, 15, TimeUnit.SECONDS);
Future<ResponseContext> task1 = done.get(0);
if (task1.isCancelled()) {
return some Error Response;
}
return task1.get();
}
Your ExecutorService should not be static, because you don't want to share it between threads for this particular use.
The Callable<ResponseContext> implementation is where the work for the long-running task is done. And as it should be obvious in the exec.invokeAll call we tell it how much we're willing to wait. The list of Futures returned will always contain as many elements as the list of tasks, so there's no need to check it for emptiness. We just have to check if the task completed or not.
You could use something like the TimeLimiter from the Google Guava library. This allows you to wrap a callable in an operation that you can call with Timeout. If the callable does not complete the operation in time, it will throw a TimeoutException which you can catch and return a 400 response.
As an example:
TimeLimiter timeLimiter = new SimpleTimeLimiter();
try {
String result = timeLimiter.callWithTimeout(
() -> doSomeHeavyWeightOperation(), 15, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// return 400
}

Recover an Asynch ThreadPoolTaskexecutor after server crashed/shut down

I Have a Spring rest controller which is calling an asynchronous method using Spring's #Async methodology and return immediately an http 202 code (Accepted) to the client.(The asynchronous job is heavy and could lead to a timeout).
So actually, at the end of the asynchronous task, i'm sending an email to the client telling him the status of his request.
Everything works just fine but I'm asking myself what can I do if my server/jvm crashes or if it is shut down? My client would receive a 202 code and will never receive a the status email.
Is there a way to synchronize (in real time) a ThreadPoolTaskExecutor in a database or even in a file to let the server recover at startup without managing this on my own with complex rules and evolution status?
Here is my Executor configuration
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("asyncTaskExecutor-");
executor.setAwaitTerminationSeconds(120);
executor.setKeepAliveSeconds(30);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
The controller that launch the async task
#RequestMapping(value = "/testAsync", method = RequestMethod.GET)
public void testAsync() throws InterruptedException{
businessService.doHeavyThings();
}
The async method called:
#Async
public void doHeavyThings() throws InterruptedException {
LOGGER.error("Start doHeavyThings with configured executor - " + Thread.currentThread().getName() + " at " + new Date());
Thread.sleep(5000L);
LOGGER.error("Stop doHeavyThings with configured executor - " + Thread.currentThread().getName() + " at " + new Date());
}
}
Thx
For a web server shutdown the application lifecycle in a java web application will notifiy a ServletContextListener. If you provide an implementation of a ServletContextListener you can put your 'what is already processed' logic in the contextDestroyed method.
When the web server or the application is started again the listener can be used to recover an re-process the unprocessed items of your job using the contextInitialized method.
Another option would be using Spring destruction callbacks and place the logic here.
HTH

Implementing threadpool in JAX-RS api

In a JAX-RS api, I want to implement a thread pool to allot new incoming requests to a new thread from the pool.
My api currently looks like this:
#Service
#Path("/")
public class SampleService {
#POST
#Path("/pass")
#Consumes(MediaType.APPLICATION_JSON)
public Response sampleApi(Incoming bean){
//do some processing on bean
File file = getUserData(bean);
//do some processing on file
return Response.status(200).entity("OK").build();
}
private File getUserData(Incoming bean){
//fetch data from external service through SOAP
//put in one file and return the file
}
}
My threadpool implementation looks like this
#Component
public class OnlineThreadPool {
private static ThreadPoolExecutor pool;
#PostConstruct
public void initialize(){
pool = new ThreadPoolExecutor(3, 20, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3));
}
public ThreadPoolExecutor getThreadPoolExecutor(){
return pool;
}
#PreDestroy
public void cleanUp(){
pool.shutdown();
}
}
I want to set up a asynchronous api so that on POST request for this api, new thread is obtained from threadpool and req is put in the new thread and response is returned without waiting for the thread to complete the processing of the request.
Any resources on how to implement this would be of great help.
EDIT:
Thanks to thilo`s answer, New thread is being created for each request but it terminates before the whole processing. But as soon as the parent thread calling this new thread terminates, child thread is also terminating. How to detach these parent child threads?
EDIT:
There was a null pointer exception in my process flow due to which thread execution was not completed. Since I have not given exception handler till now it was not showing any errors, just stopping without any exception message.
#POST
#Path("/pass")
#Consumes(MediaType.APPLICATION_JSON)
public Response sampleApi(final Incoming bean){
onlineThreadPool.getThreadPoolExecutor().submit(new Runnable(){
#Override public void run(){
//do some processing on bean
File file = getUserData(bean);
//do some processing on file
}});
return Response.status(200).entity("OK").build();
}
This is how it should look like. Notice that I am using constructor injection to inject your OnlineThreadPool dependency and Java 8 lambda for creating the Runnable.
#Service
#Path("/")
public class SampleService {
private OnlineThreadPool pool;
public SampleService(OnlineThreadPool pool) {
this.pool = pool;
} //constructor injection
#POST
#Path("/pass")
#Consumes(MediaType.APPLICATION_JSON)
public Response sampleApi(Incoming bean){
pool.getThreadPoolExecutor().submit(() -> {
File file = getUserData(bean);
//some other processing
});
return Response.status(200).entity("OK").build();
}
}

Categories

Resources