如何在关机时等待RestTemplate响应?

我正在使用带RestTemplatePOST请求发送到网络服务器。

当我的应用程序关闭时(例如从tomcat取消部署),应该延迟关闭,直到收到所有挂起的响应(在超时内)。

restTemplate在引擎盖下使用HttpComponentsClientHttpRequestFactory

问题:如何告诉spring延迟关机? @PreDestroy可能是一种可能,但我如何检测restTemplate上的待处理请求?

我认为没有开箱即用的解决方案,如https://github.com/spring-projects/spring-boot/issues/4657中所述

对于下面的Tomcat代码应该可行

 @Component @Scope("singleton") public class ApplicationContextClosedListener implements ApplicationListener, 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(); } } } } 

您可以使用ExecutorService执行所有请求,然后添加@PreDestroy挂钩以等待在给定超时内完成所有任务。 您的服务可以是这样的

 @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."); } } 

测试这个的简单方法是运行下面的应用程序并在某个时候终止它。

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

如果您正常关闭应用程序,您将看到如果将请求发送到delay endpoint ,则executorService将等待最多3秒钟以完成任务,然后终止该组件。 executorService.shutdown()启动关闭,在该关闭中执行先前提交的任务,但不接受任何新任务。

此代码使用带有嵌入式tomcat的spring-boot,但是相同的方法可以应用于任何spring应用程序上下文。