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

时间:2017-11-30 09:35:04

标签: java spring spring-mvc resttemplate

我正在使用spring RestTemplate向网络服务器发送POST个请求。

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

restTemplate底层使用HttpComponentsClientHttpRequestFactory

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

2 个答案:

答案 0 :(得分:0)

我认为https://github.com/spring-projects/spring-boot/issues/4657

中没有开箱即用的解决方案

对于下面的Tomcat代码应该可以工作

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

答案 1 :(得分:0)

您可以使用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 endpointexecutorService将等待最多3秒钟才能完成任务,然后终止组件。 executorService.shutdown()启动关闭,其中先前提交的任务被执行,但不会接受任何新任务。

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