简体   繁体   English

CompletableFuture join() 方法中的巨大延迟

[英]Huge delay in CompletableFuture join() method

So I'm working on an application that has to make 20+ HTTP calls at a time.所以我正在开发一个必须一次进行 20 多个 HTTP 调用的应用程序。 Each one of them takes 2-3 seconds to get a response.他们每个人都需要 2-3 秒才能得到响应。 It's pretty slow to make these calls one at a time (40 seconds at best), so I am trying to send them asynchronously via CompletableFutures.一次进行一次这些调用(最多 40 秒)非常慢,因此我尝试通过 CompletableFutures 异步发送它们。 This should allow me to make calls while I'm waiting for the response of others, in theory reducing the total time to maybe 4-5 seconds instead of 40.这应该允许我在等待其他人的响应时拨打电话,理论上将总时间减少到 4-5 秒而不是 40 秒。

I made a very similar setup to this tutorial I found at https://www.codepedia.org/ama/how-to-make-parallel-calls-in-java-with-completablefuture-example .我做了一个与我在https://www.codepedia.org/ama/how-to-make-parallel-calls-in-java-with-completablefuture-example 上找到的教程非常相似的设置。

import org.codingpedia.example;

import javax.inject.Inject;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class ParallelCallsDemoService {

    @Inject
    RestApiClient restApiClient;

    private ExecutorService es = Executors.newFixedThreadPool(20);

    public List<ToDo> getToDos(List<String> ids){

        List<CompletableFuture<ToDo>> futures =
                ids.stream()
                          .map(id -> getToDoAsync(id))
                          .collect(Collectors.toList());

        List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

        return result;
    }


    CompletableFuture<ToDo> getToDoAsync(String id){

        CompletableFuture<ToDo> future = CompletableFuture.supplyAsync(() -> {
            return restApiClient.makeSomeHttpCall(id);
        }, es);

        return future;
    }

}

By all accounts it seems to be working - the calls all get sent at roughly the same time, and they all return in a couple seconds.从各方面来看,它似乎都在工作 - 调用都大致在同一时间发送,并且它们都在几秒钟内返回。 But then I'm experiencing a huge delay of 30-40 seconds on this part:但是后来我在这部分遇到了 30-40 秒的巨大延迟:

        List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

This makes it take roughly the same time as sending serially, which baffles me.这使得它与串行发送的时间大致相同,这让我感到困惑。 How can it be that I'm getting all responses in a couple seconds but then there's a 30 second delay on joining them?我怎么会在几秒钟内收到所有回复,但加入他们后会有 30 秒的延迟? It's almost as if (despite appearances) they're still being made serially.就好像(尽管出现)它们仍然是连续制作的。 Why does the join take so long?为什么加入需要这么长时间?

There's a bit of a problem here这里有点问题

List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

I think the stream you are using is not a parallel stream.我认为您使用的流不是并行流。 Therefore, each call to map is waiting for the last call to finish.因此,对 map 的每次调用都在等待最后一次调用完成。 Changing futures.stream() to futures.parallelStream() should have an improvement.futures.stream()更改为futures.parallelStream()应该会有改进。 Of course if you are not using a machine with a single core.当然,如果您使用的不是单核机器。

Finally figured this out!终于想通了! Thanks everyone for your suggestions.谢谢大家的建议。 Turns out it was nothing to do with my implementation of CompletableFutures.结果证明这与我对 CompletableFutures 的实现无关。 When I receive a response from the service, I use JAXB to convert the java object to an XML string for logging purposes.当我收到来自服务的响应时,我使用 JAXB 将 java 对象转换为 XML 字符串以进行记录。 I started looking at thread dumps when it was hanging, and realized it was actually the JAXB string conversion that the threads were waiting for (the response object is quite large).我在线程挂起时开始查看线程转储,并意识到它实际上是线程正在等待的 JAXB 字符串转换(响应对象非常大)。 I took out that part, and the performance immediately improved to what it should have been.我取出那部分,性能立即提高到应有的水平。

we have encountered a similar problem.我们遇到了类似的问题。 we have solved it using '.get(timeout)' method of CompletableFuture.我们已经使用 CompletableFuture 的 '.get(timeout)' 方法解决了它。

CompletableFuture[] array = (CompletableFuture[]) futures.toArray();
       try {
        CompletableFuture.allOf(array).get(180, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        //log error
    }

Place the timeout based on your real-time results.根据您的实时结果设置超时。 you can tweak the time using an external configuration.您可以使用外部配置调整时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM