在程序退出之前执行的非阻塞CompleteableFuture

时间:2018-08-27 16:09:30

标签: java concurrency completable-future

我正在执行一个应用程序(static void main(String[] args),它以特定的节奏异步向端点发送POST请求。由于请求的数量和应用程序的运行时间过长,这是不可行的将期货保存在列表中,然后事后进行处理。

因此,我添加了一个thenAcceptAsync调用,该调用仅记录响应的传入。但是,特别是对于较短的测试运行,该程序将在返回某些响应之前完成并退出。

如何确保在程序完成之前记录所有响应?

public void replayScripts() {
    final String applicationId = getSparkApplicationId();
    LOGGER.debug("applicationId: {}", applicationId);

    final long scriptsToSubmit = this.config.getMaxScripts() == null ? this.config.getScripts().size()
            : this.config.getMaxScripts().intValue();
    long maxWaitTimeMillis = 0L;
    long actualWaitTimeMillis = 0L;

    for (int i = 0; i < scriptsToSubmit; i++) {
        LOGGER.info("submitting script #{} of {}", i + 1, scriptsToSubmit);

        this.dao.submitScript(this.config.getScripts().get(i).getRawScript())
                .thenAcceptAsync(this::logResponse);

        if (i + 1 < this.config.getScripts().size()) {
            final long millisWait = TimeUnit.MILLISECONDS.convert(
                    Duration.between(this.config.getScripts().get(i).getSubmissionTime(),
                            this.config.getScripts().get(i + 1).getSubmissionTime()).get(ChronoUnit.NANOS),
                    TimeUnit.NANOSECONDS);

            maxWaitTimeMillis += millisWait;
            actualWaitTimeMillis += sleep(millisWait, applicationId);

            if (this.config.getClearCache()) {
                this.dao.clearCache();
            }
        }
    }

    LOGGER.info("max wait time: {}s", maxWaitTimeMillis / 1000.0);
    LOGGER.info("actual wait time: {}s", actualWaitTimeMillis / 1000.0);
}

编辑:解决方案

根据@irahavoi的建议,我暂时采用了计数器解决方案,并添加了此代码段,该代码段使用AtomicInteger类成员来跟踪记录的响应数:

    while (this.config.isLogResponses() && this.loggedResponsesCounter.get() < scriptsToSubmit) {
        try {
            LOGGER.info("sleeping for one second to allow all responses to be logged");
            Thread.sleep(1000);
        } catch (final InterruptedException e) {
            LOGGER.warn("An exception occurred while attempting to wait for all responses to be logged", e);
        }
    }

2 个答案:

答案 0 :(得分:1)

我看到了两个解决方案(都不是很优雅):

  1. 将请求拆分为多个批次(例如,每个批次为1k个请求)。并分别处理每批。这样一来,您就可以使用它,而不会出现OOM的风险:

    CompletableFuture.allOf(aBatchOfLogPipelineResponses).join();

  2. 如上面的评论中所述,您可以为接收到的响应创建一个计数器(每次收到响应,它就会递增)。并定期在主线程中检查它:如果计数器的值等于scriptsToSubmit,则可以退出。

答案 1 :(得分:1)

另一个更干净的解决方案是关闭ExecutorService,然后等待其终止。

我认为您已经在DAO中使用了自定义的ExecutorService,因为CompletableFuture默认使用的公用池不适合IO任务。否则,您应该查看Executors中的工厂方法。

因此,一旦提交了所有任务,就应该在主线程上调用ExecutorService.shutdown(),然后调用awaitTermination()

这将等待所有任务(包括排队的任务)完成执行。