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!
Related
The following daemon-bean is running:
public class DaemonBean extends Thread {
private final static Logger log = LoggerFactory.getLogger(DaemonBean.class);
{
setDaemon(true);
start();
}
#Override
public void run() {
for(int i=0; i<10 && !isInterrupted(); ++i) {
log.info("Hearbeat {}", i);
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
It is daemon, so would terminate if singleton.
So, the following non-daemon bean is waiting for him:
public class Waitor1 extends Thread {
private final static Logger log = LoggerFactory.getLogger(Waitor1.class);
private Thread joinable;
{
setDaemon(false);
setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Error in thread", e);
}
});
}
public Thread getJoinable() {
return joinable;
}
public void setJoinable(Thread value) {
this.joinable = value;
if( this.joinable != null ) {
start();
}
}
#Override
public void run() {
log.info("Waiting started");
try {
joinable.join();
} catch (InterruptedException e) {
log.info("Thread interrupted");
return;
}
log.info("Waiting ended");
}
}
The Spring configuration for beans is:
<bean id="daemon" class="beans.DaemonBean"/>
<bean id="waitor" class="beans.Waitor1">
<property name="joinable" ref="daemon"/>
</bean>
The question is: why is it working if runned from main and not working if ran from jUnit test?
Running code is
public static void main(String[] args) {
new ClassPathXmlApplicationContext("/beans/Waiting1.xml");
}
or
#Test
public void testWaiting1() {
new ClassPathXmlApplicationContext("/beans/Waiting1.xml");
}
In case of main I see all hearbeats. In case of jUnit I see only heartbeat 0, then message "Waiting started" and the program is terminated as if nobody waiting for non-daemon threads here.
What can be the reason of it?
When you run your code from main it creates both beans, thus two threads - daemon and non-daemon. As long as non-daemon thread is running, your application won't exit. So it works.
It's different when run from JUnit. As soon as JUnit test method completes (and it completes immediately after the Spring context is up), JUnit assumes your tests are done. Thus it kills all your threads and basically the whole JVM.
Remember your Waitor1 bean spawns a background thread which JUnit doesn't care about. As soon as you leave #Test method JUnit will just stop everything.
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'm using a few services inheriting from the AbstractScheduledService, which get managed by a ServiceManager. Everything works fine, but now, there's a service whose runOneIteration takes a rather long time, and as the result, my process takes too long to terminate (more than five seconds).
There are other services inheriting from AbstractExecutionThreadService, which had a similar problem, which I could solve via
#Override
protected final void triggerShutdown() {
if (thread != null) thread.interrupt();
}
and storing private volatile thread in the run method. However, there's no triggerShutdown for AbstractScheduledService as stated in this issue.
I already considered alternatives like making runOneIteration do less work, but it's both ugly and inefficient.
I can't override stopAsync as it's final and I can't see anything else. Is there a hook for doing something like this?
Can you work with this? Was there any reason you couldn't add a triggerShutdown yourself?
class GuavaServer {
public static void main(String[] args) throws InterruptedException {
GuavaServer gs = new GuavaServer();
Set<ForceStoppableScheduledService> services = new HashSet<>();
ForceStoppableScheduledService ts = gs.new ForceStoppableScheduledService();
services.add(ts);
ServiceManager manager = new ServiceManager(services);
manager.addListener(new Listener() {
public void stopped() {
System.out.println("Stopped");
}
public void healthy() {
System.out.println("Health");
}
public void failure(Service service) {
System.out.println("Failure");
System.exit(1);
}
}, MoreExecutors.directExecutor());
manager.startAsync(); // start all the services asynchronously
Thread.sleep(3000);
manager.stopAsync();
//maybe make a manager.StopNOW()?
for (ForceStoppableScheduledService service : services) {
service.triggerShutdown();
}
}
public class ForceStoppableScheduledService extends AbstractScheduledService {
Thread thread;
#Override
protected void runOneIteration() throws Exception {
thread = Thread.currentThread();
try {
System.out.println("Working");
Thread.sleep(10000);
} catch (InterruptedException e) {// can your long process throw InterruptedException?
System.out.println("Thread was interrupted, Failed to complete operation");
} finally {
thread = null;
}
System.out.println("Done");
}
#Override
protected Scheduler scheduler() {
return Scheduler.newFixedRateSchedule(0, 1, TimeUnit.SECONDS);
}
protected void triggerShutdown() {
if (thread != null) thread.interrupt();
}
}
}
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.
The following daemon-bean is running:
public class DaemonBean extends Thread {
private final static Logger log = LoggerFactory.getLogger(DaemonBean.class);
{
setDaemon(true);
start();
}
#Override
public void run() {
for(int i=0; i<10 && !isInterrupted(); ++i) {
log.info("Hearbeat {}", i);
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
It is daemon, so would terminate if singleton.
So, the following non-daemon bean is waiting for him:
public class Waitor1 extends Thread {
private final static Logger log = LoggerFactory.getLogger(Waitor1.class);
private Thread joinable;
{
setDaemon(false);
setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Error in thread", e);
}
});
}
public Thread getJoinable() {
return joinable;
}
public void setJoinable(Thread value) {
this.joinable = value;
if( this.joinable != null ) {
start();
}
}
#Override
public void run() {
log.info("Waiting started");
try {
joinable.join();
} catch (InterruptedException e) {
log.info("Thread interrupted");
return;
}
log.info("Waiting ended");
}
}
The Spring configuration for beans is:
<bean id="daemon" class="beans.DaemonBean"/>
<bean id="waitor" class="beans.Waitor1">
<property name="joinable" ref="daemon"/>
</bean>
The question is: why is it working if runned from main and not working if ran from jUnit test?
Running code is
public static void main(String[] args) {
new ClassPathXmlApplicationContext("/beans/Waiting1.xml");
}
or
#Test
public void testWaiting1() {
new ClassPathXmlApplicationContext("/beans/Waiting1.xml");
}
In case of main I see all hearbeats. In case of jUnit I see only heartbeat 0, then message "Waiting started" and the program is terminated as if nobody waiting for non-daemon threads here.
What can be the reason of it?
When you run your code from main it creates both beans, thus two threads - daemon and non-daemon. As long as non-daemon thread is running, your application won't exit. So it works.
It's different when run from JUnit. As soon as JUnit test method completes (and it completes immediately after the Spring context is up), JUnit assumes your tests are done. Thus it kills all your threads and basically the whole JVM.
Remember your Waitor1 bean spawns a background thread which JUnit doesn't care about. As soon as you leave #Test method JUnit will just stop everything.