How to wait on RestTemplate responses on shutdown? - java

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.

Related

Stop thread that's already running

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

About Spring Cloud Stream Application shutdown gracefully

I'm using Spring Cloud Stream as my service communication framework. But how can I shutdown the application without force terminating the working thread( produced #StreamListener).
I wrote some codes to meet requirement, but dependent on a Map. I don't think it a good resolution.
#SpringBootApplication
#EnableBinding(Sink.class)
public class MessageReceiverApplication {
private static final Logger logger = LoggerFactory.getLogger(MessageReceiverApplication.class);
private AtomicLong atomicLong = new AtomicLong();
private Map<Long, Void> map = new ConcurrentHashMap<>();
#StreamListener(Sink.INPUT)
#Transactional(rollbackFor = Exception.class)
public void process(Message<String> message) {
long count = atomicLong.incrementAndGet();
try {
map.put(count, null);
logger.info("Processing:\t[{}]", count);
Thread.sleep(new Random().nextInt(5000));
logger.info(" Done:\t[{}]", count);
} catch (InterruptedException e) {
logger.error("Terminate Exceptionally");
} finally {
map.remove(count);
}
}
#PostConstruct
public void init() {
logger.info("Start listening");
}
#PreDestroy
public void tearDown() throws InterruptedException {
logger.info("Closing...");
while(map.size() > 0) {
logger.warn("Waiting for Stream job done");
Thread.sleep(500);
}
}
public static void main(String[] args) {
SpringApplication.run(MessageReceiverApplication.class, args);
}
}
In this example, I start application by "java -jar", and CTRL+C to terminate the application. The application will terminate until the process() method finished, because of while(map.size() > 0) to check the Stream Listener Thread.
I'm wondering if there is a more graceful way to shutdown the thread without Interrupt the code within #StreamListener. Thanks a lot!

Threadpool task executor timeout for working thread

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.

Correct handling of background calls in jersey

I have to do some background calls to a web service within a web application. The response of the call is not really time-critical and barely interesting for me. It changes only in rare cases, where I will react to it by throwing a exception, or logging the failure or something.
My conrete question now refers to the correct handling of such asynch calls in Jersey Client 2.
Option 1 uses the async calls from jersey, but then starts a thread for each response.
Option 2 would immediatly start a thread and use jersey from inside this thread.
Generally which option would be more adviseable?
Option 1
private static final ExecutorService executorService = Executors.newFixedThreadPool(20);
--
Client client = ClientBuilder.newClient();
WebTarget target = client.target("somehost").path("somepath");
final AsyncInvoker asyncInvoker = target.request().async();
final Future<Response> responseFuture = asyncInvoker.post(Entity.json(myjsonobject));
executorService.execute(new Worker(responseFuture));
-- Worker Thread
public class Worker implements Runnable {
private Future<Response> futureResponse;
public Worker(Future<Response> futureResponse){
this.futureResponse = futureResponse;
}
#Override
public void run() {
try {
final Response response = futureResponse.get();
if(response.getStatus() == 500)
doSomething();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Option 2
private static final ExecutorService executorService = Executors.newFixedThreadPool(20);
--
executorService.execute(new Worker());
-- Worker Thread
public class Worker implements Runnable {
public Worker(){
}
#Override
public void run() {
try {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("somehost").path("somepath");
ClientResponse response = target.request().accept(MediaType.APPLICATION_JSON).post(Entity.json(myjsonobject), ClientResponse.class);
if(response.getStatus() == 500) {
doSomething();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
Instead of trying to implement a "non-blocking" Future.get() (with another thread), you can simply make use of the InvocationCallback, passing an instance to the get method. For example
Future<Response> future = target.request().async().get(new InvocationCallback<Response>(){
#Override
public void completed(Response response) {
System.out.println(response.readEntity(String.class));
response.close();
client.close();
}
#Override
public void failed(Throwable throwable) { /** Log exception **/ }
});
See more at Asynchronous Client Callbacks

Runnables in Spring's Task Scheduler

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())
}
}

Categories