I am using spring boot and have one async method. To execute async I have below configuration, questions is what if all those 5 thread hangs for some reason , essentially it will lock the application and none of new task will be executed (it will just keep accepting). How we can set timeout for those working thread , lets say 120 seconds, so after that it timesout and execute new task. (Yes I am using fixed thread pool with unbounded queue to keep accepting tasks)
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(5);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
You can create another executor like:
static class TimeOutExecutorService extends CompletableExecutors.DelegatingCompletableExecutorService {
private final Duration timeout;
private final ScheduledExecutorService schedulerExecutor;
TimeOutExecutorService(ExecutorService delegate, Duration timeout) {
super(delegate);
this.timeout = timeout;
schedulerExecutor = Executors.newScheduledThreadPool(1);
}
#Override public <T> CompletableFuture<T> submit(Callable<T> task) {
CompletableFuture<T> cf = new CompletableFuture<>();
Future<?> future = delegate.submit(() -> {
try {
cf.complete(task.call());
} catch (CancellationException e) {
cf.cancel(true);
} catch (Throwable ex) {
cf.completeExceptionally(ex);
}
});
schedulerExecutor.schedule(() -> {
if (!cf.isDone()) {
cf.completeExceptionally(new TimeoutException("Timeout after " + timeout));
future.cancel(true);
}
}, timeout.toMillis(), TimeUnit.MILLISECONDS);
return cf;
}
}
Then, create a new bean named timed
#Bean(name = "timed")
public Executor timeoutExecutor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("timed-%d").build();
return TimedCompletables.timed(Executors.newFixedThreadPool(10, threadFactory), Duration.ofSeconds(2));
}
And, try to use this Executor to execute your async tasks.
Or, try to change your code from FixSizeThreadPool to build a own thread pool executor.
You can not submit some task with timeout. What you can do is when you submit your task your get a Future object. You can keep this reference in some Map and pole and see if the task keeps running past your timeout. If so you can use the method cancel() of class Future.
Alternatively, your own task when it starts running places its own current thread into some Map visible to your main (submitting) thread. Also if you see that your task didn't finish in time (again poling) you can try and interrupt your thread. In either case your submitted task should be able to react to interrupt() method of Thread class. I actually implemented this alternative way. If you go this way, test a LOT... :)
I think Future.get(timeout, unit) method can manage async timeout.
Following example can work on my local.
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class AsyncTimeoutExampleAppliation {
private final MyService myService;
public AsyncTimeoutExampleAppliation(MyService myService) {
this.myService = myService;
}
public static void main(String[] args) {
SpringApplication.run(AsyncTimeoutExampleAppliation.class, args);
}
#PostConstruct
void postConstract(){
asyncCall();
}
public void asyncCall(){
try {
String result = myService.doSomething()
.get(10, TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
#Service
public static class MyService {
#Async
public Future<String> doSomething() throws InterruptedException {
TimeUnit.SECONDS.sleep(60);
return CompletableFuture.completedFuture("Finished");
}
}
}
We can get TimeoutException 10 seconds after application starts.
Related
I am writing a custom bounded scheduler. This should be able to schedule tasks with a given delay. Try to schedule tasks if the bound has been reached, throwing an exception after a timeout. Here is what I have so far:
public class BoundedScheduledExecutor {
private final ScheduledThreadPoolExecutor executor;
private final Semaphore semaphore;
private final int maxWaitSeconds;
private static final Logger LOG = LoggerFactory.getLogger(
BoundedScheduledExecutor.class
);
// constructor omitted
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit)
throws Exception {
try {
boolean result = semaphore.tryAcquire(this.maxWaitSeconds, TimeUnit.SECONDS);
LOG.info("result {}", result);
LOG.info("executor {}", this.executor.getActiveCount());
LOG.info("semaphore {}", this.semaphore);
if (result) {
return this.executor.schedule(
() -> {
try {
LOG.info("before run");
task.run();
} finally {
semaphore.release();
}
},
delay,
unit
);
} else {
semaphore.release();
throw new RejectedExecutionException();
}
} catch (RejectedExecutionException e) {
semaphore.release();
throw e;
} catch (InterruptedException e) {
throw e;
}
}
}
The code is based on a similar implementation from Java concurrency in practice. In my test, I am trying to verify that if I have a short task and another task following that, the second one is scheduled once the short task finishes. My test is this:
public void itSchedulesAfterTimeoutWhenQueueIsFull() throws Exception {
this.boundedScheduledExecutor = new BoundedScheduledExecutor(executor, 1, 3);
Runnable blockingTask = new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {}
}
};
Runnable simpleTask = new Runnable() {
#Override
public void run() {}
};
this.boundedScheduledExecutor.schedule(blockingTask, 0, TimeUnit.SECONDS);
this.boundedScheduledExecutor.schedule(simpleTask, 5, TimeUnit.SECONDS);
}
But the second call to schedule throws an exception because tryAcquire returns false. I expect my test should succeed because the first task takes 1s and is scheduled right away. The second one is scheduled after 5s, I was expecting the semaphore will be released in between so that the second task can proceed. I noticed that the semaphore is not released in the finally block for the scheduled task. How do I get the semaphore to release after a task is complete? Why doesn't the finally block execute?
return this.executor.schedule(
() -> {
try {
LOG.info("before run");
task.run();
} finally {
semaphore.release();
}
},
delay,
unit
);
This code does not execute the lambda, it just sends it to the executor that will execute it at some time.
Even if the executor starts running the lambda as soon as it receives it, your main code will not wait for it and the return will be applied, even if the lambda is still running the task.
So it may very well be that the first scheduled task still has not been able to release the semaphore before you invoke this method again, unless you are adding additional controls outside of this code.
In fact with your code you cannot have more than one task scheduled at a given time; until the task currently scheduled has not being finished you will not release the semaphor, but you require acquiring it to schedule a new task.
Faced the fact that when the database is unavailable, the queue grows because tasks stop running. What is the best way to set some timeout for tasks executed in method run()? May be there is some good approach with using ExecutorService?
#Service
public class AsyncWriter implements Writer, Runnable {
private LinkedBlockingQueue<Entry> queue = new LinkedBlockingQueue<>();
private volatile boolean terminate = false;
private AtomicInteger completedCounter = new AtomicInteger();
#PostConstruct
private void runAsyncWriter() {
Thread async = new Thread(this);
async.setName("Writer Thread");
async.setPriority(2);
async.start();
}
#Override
public void run() {
while (!terminate) {
try {
Entry entry = queue.take();
dao.save(entry);
completedCounter.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void write(Entry entry) {
queue.add(entry);
}
}
Maybe you can try RxJava
https://www.baeldung.com/rx-java
And you can set your aync funtions
Timeout in RxJava
I have two APIs: one starts the thread, and another stops the thread. I'm successfully able to start a thread by calling /start API, but I'm unable to stop already running thread by calling /stop API. Seems like Executor#stop() does nothing.
My RestController:
#Autowired
private Executor executor;
#RequestMapping(path = "/start", method = GET)
public ResponseEntity<HttpStatus> startLongTask() {
executor.start();
return ResponseEntity.ok(HttpStatus.OK);
}
#RequestMapping(path = "/stop", method = GET)
public ResponseEntity<HttpStatus> stopLongTask() {
executor.stop();
return ResponseEntity.ok(HttpStatus.OK);
}
My Executor:
#Component
public class Executor {
#Value("${threads.number}")
private int threadsNumber;
private ExecutorService executorService;
#Autowired
private OtherService otherService;
#PostConstruct
private void init() {
executorService = Executors.newFixedThreadPool(threadsNumber);
executorService = Executors.newScheduledThreadPool(threadsNumber);
}
/**
* Start.
*/
public void start() {
executorService.submit(() -> otherService.methodImExecuting());
}
/**
* Stop.
*/
#PreDestroy
publicvoid stop() {
executorService.shutdownNow();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
}
Here's the methodImExecuting:
#Component
public class OtherService {
public void methodImExecuting() {
List<SomeObject> dataList = repository.getDataThatNeedsToBeFilled();
for (SomeObject someObject : dataList) {
gatewayService.sendDataToOtherResourceViaHttp(someObject);
}
}
}
Short answer: You can not stop a running thread which does not cooperate. There's a deprecated destroy() method for threads, but this will lead to a "bad" state of your VM.
The only possibility to end the Thread clean is to interrupt it. But to check for interruption is the task of the thread itself.
So your methodImExcecuting sould look like:
void methodImExecuting() throws InterruptedException {
// it depends on your implementation, I assume here that you iterate
// over a collection for example
int loopCount = 0;
for (Foo foo : foos) {
++loopCount;
if (loopCount % 100 == 0) {
if (Thread.interrupted())
throw new InterruptedException();
}
...
}
It depends on your implementation how often you have to look if your thread was interrupted. But it's a fact that the call of executorService.shutdownNow(); will only set the interrupted flag of all threads currently running in the executorService. To really interrupt the thread, the thread must itself check if the interrupted flag is set and then throw an InterruptedException
Your running threads have to react to the interrupt signal
Thread.currentThread().isInterrupted()
Otherwise the sending of the interrupt signal has no effect.
Here you can find a good explanation:
Difference between shutdown and shutdownNow of Executor Service
I'm using spring with RestTemplate to send POST requests to a webserver.
When my application is shut down (eg undeployed from tomcat), the shutdown should be delayed until all pending responses are received (within a timeout).
The restTemplate uses HttpComponentsClientHttpRequestFactory under the hood.
Question: how can I tell spring to delay the shutdown? #PreDestroy could be one possibility, but how can I detect pending requests on the restTemplate?
I think there is no out of the box solution as stated in https://github.com/spring-projects/spring-boot/issues/4657
For Tomcat code below should work
#Component
#Scope("singleton")
public class ApplicationContextClosedListener implements ApplicationListener<ContextClosedEvent>, TomcatConnectorCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextClosedListener.class);
private volatile Connector connector;
#Value("${timeout}")
private Integer timeout;
#Override
public void customize(Connector connector) {
this.connector = connector;
}
#Override
public void onApplicationEvent(ContextClosedEvent event) {
if (connector != null) {
shutdownGracefully();
}
}
private void shutdownGracefully() {
connector.pause();
Executor executor = connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
try {
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(timeout, TimeUnit.SECONDS)) {
LOGGER.warn("Shutdown: Tomcat thread pool did not shut down gracefully within specified period. Proceeding with forceful shutdown");
}
threadPoolExecutor.shutdownNow();
LOGGER.info("Shutdown: the executor shutdown completed");
} catch (InterruptedException ex) {
LOGGER.error("Shutdown: Interrupt signal received");
threadPoolExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}
You can execute all the requests with ExecutorService and then add a #PreDestroy hook to wait for all tasks to be completed within a given timeout. Your service can be like this
#Slf4j
#Component
public class SenderService {
private static final int TWO_SECONDS = 2;
private RestTemplate restTemplate;
private ExecutorService executorService;
public SenderService() {
this.restTemplate = new RestTemplate();
this.executorService = Executors.newFixedThreadPool(1);
}
public void sendRequest() throws Exception {
executorService.submit(() -> {
ZonedDateTime now = ZonedDateTime.now();
log.info("Sending request at {} ...", now);
restTemplate.getForObject("https://httpbin.org/delay/{delay}", Void.class, TWO_SECONDS, now);
log.info("Response received for request at {}", now);
return null;
}).get();
}
#PreDestroy
public void destroy() throws InterruptedException {
log.info("Shutting down sender service...");
executorService.shutdown();
executorService.awaitTermination(3, TimeUnit.SECONDS);
log.info("Sender service terminated.");
}
}
The simple way to test this is running the application below and terminating it at some point.
#SpringBootApplication
public class Application {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
SenderService senderService = run.getBean(SenderService.class);
while (true) {
senderService.sendRequest();
}
}
}
If you gracefully shut down the application, you'll see that if a request is sent to delay endpoint, the executorService is going to wait up to 3 seconds for the task to be completed and then terminate the component. executorService.shutdown() initiates a shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
This code is using spring-boot with embedded tomcat, but the same approach could be applied to any spring application context.
I'm using the below snippet in Spring to schedule job executions. A job is found by querying the database. Once a new job is found (which has not yet been scheduled), it is scheduled programmatically.
My question is, is this the way to create Runnables for task execution? Is it accessing taskScheduler in the right way or should it access it by fetching an instance from application context?
#Service
public class TimeOfDayScheduler {
#Autowired
private JobExecutor jobExecutor;
#Autowired
private TaskScheduler taskScheduler;
#Scheduled(cron = "${scheduler.interval}")
#PostConstruct
public void findNewJobs() {
// Checks the database if any new jobs should be scheduled
// ...
// If found:
scheduleJob(somejob);
// ...
}
public void scheduleJob(final Job job) {
Runnable task = new Runnable() {
#Override
public void run() {
try {
jobExecutor.execute(job);
} catch (Exception e) {
logger.error("Scheduler error", e);
}
}
};
taskScheduler.schedule(task, new CronTrigger(job.getCronPattern())
}
}