【问题标题】:Why doesn't this thread pool execute HTTP requests simultaneously?为什么这个线程池不同时执行 HTTP 请求?
【发布时间】:2018-10-21 16:12:10
【问题描述】:

我写了几行代码,它将向我机器上运行的服务发送 50 个 HTTP GET 请求。该服务将始终sleep 1 秒,并返回带有空正文的 HTTP 状态代码 200。正如预期的那样,代码运行了大约 50 秒。

为了加快速度,我尝试创建一个有 4 个线程的 ExecutorService,这样我就可以始终同时向我的服务发送 4 个请求。我预计代码会运行大约 13 秒。

final List<String> urls = new ArrayList<>();
for (int i = 0; i < 50; i++)
    urls.add("http://localhost:5000/test/" + i);

final RestTemplate restTemplate = new RestTemplate();

final List<Callable<String>> tasks = urls
        .stream()
        .map(u -> (Callable<String>) () -> {
            System.out.println(LocalDateTime.now() + " - " + Thread.currentThread().getName() + ": " + u);
            return restTemplate.getForObject(u, String.class);
        }).collect(Collectors.toList());

final ExecutorService executorService = Executors.newFixedThreadPool(4);

final long start = System.currentTimeMillis();
try {
    final List<Future<String>> futures = executorService.invokeAll(tasks);

    final List<String> results = futures.stream().map(f -> {
        try {
            return f.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException(e);
        }
    }).collect(Collectors.toList());
    System.out.println(results);
} finally {
    executorService.shutdown();
    executorService.awaitTermination(10, TimeUnit.SECONDS);
}

final long elapsed = System.currentTimeMillis() - start;
System.out.println("Took " + elapsed + " ms...");

但是 - 如果您查看调试输出的秒数 - 似乎前 4 个请求是同时执行的,但所有其他请求都一个接一个地执行:

2018-10-21T17:42:16.160 - pool-1-thread-3: http://localhost:5000/test/2
2018-10-21T17:42:16.160 - pool-1-thread-1: http://localhost:5000/test/0
2018-10-21T17:42:16.160 - pool-1-thread-2: http://localhost:5000/test/1
2018-10-21T17:42:16.159 - pool-1-thread-4: http://localhost:5000/test/3
2018-10-21T17:42:17.233 - pool-1-thread-3: http://localhost:5000/test/4
2018-10-21T17:42:18.232 - pool-1-thread-2: http://localhost:5000/test/5
2018-10-21T17:42:19.237 - pool-1-thread-4: http://localhost:5000/test/6
2018-10-21T17:42:20.241 - pool-1-thread-1: http://localhost:5000/test/7
...
Took 50310 ms...

因此出于调试目的,我将 HTTP 请求更改为 sleep 调用:

// return restTemplate.getForObject(u, String.class);
TimeUnit.SECONDS.sleep(1);
return "";

现在代码按预期工作了:

...
Took 13068 ms...

所以我的问题是为什么带有睡眠调用的代码可以按预期工作而带有 HTTP 请求的代码却不能?我怎样才能让它以我预期的方式运行?

【问题讨论】:

  • 可能是因为前四个线程很忙,所以之后就没有并行了。 "sleep(1)" 基本上没有时间,所以它立即结束。您的处理显然比“sleep(1)”花费的时间长得多。
  • @markspace 没有额外的处理, sleep(1) 真的是指一秒:plackup -e 'sub { sleep 1; return [200, ["Content-Type" =&gt; "text/plain"], [""]] }'
  • 另一端有多少线程?
  • 我猜问题是服务器逻辑(您的业务服务)。看起来您的服务器不会同时提供多个请求。你可以把你的服务器代码吗?

标签: java http threadpool executorservice resttemplate


【解决方案1】:

从信息中,我可以看出这是最可能的根本原因:

您发出的请求是并行完成的,但满足这些请求的 HTTP 服务器一次处理 1 个请求

因此,当您开始发出请求时,executor service fires up the requests concurrently,因此您会同时获得前 4 个。

HTTP 服务器 可以响应一次一个请求,即每次 1 秒后。

现在,当第一个请求得到满足时,执行器服务会选择另一个请求并触发它,这会一直持续到最后一个请求。

在 HTTP 服务器上一次阻止 4 个请求,这些请求被串行一个接一个地提供服务。

要获得该理论的Proof of Concept,您可以使用可以同时从 4 个通道接收测试的消息传递服务(队列)。这应该会减少时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 2017-10-25
    • 2011-12-05
    • 1970-01-01
    • 2021-12-27
    • 2015-05-16
    相关资源
    最近更新 更多