Почему этот пул потоков не выполняет HTTP-запросы одновременно? - PullRequest
0 голосов
/ 21 октября 2018

Я написал несколько строк кода, которые будут отправлять 50 HTTP-запросов GET службе, работающей на моей машине.Служба всегда будет sleep 1 секунду и вернет код состояния HTTP 200 с пустым телом.Как и ожидалось, код выполняется около 50 секунд.

Чтобы немного ускорить процесс, я попытался создать 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 - нет?И как мне заставить его вести себя так, как я ожидал?

1 Ответ

0 голосов
/ 21 октября 2018

Из информации я вижу, что это наиболее вероятная основная причина:

Запросы, которые вы делаете, выполняются параллельно, но сервер HTTP , который выполняет эти запросы обрабатывает 1 запрос за раз .

Таким образом, когда вы начинаете делать запросы, executor service fires up the requests concurrently, таким образом, вы получаете первые 4 одновременно.

Но HTTP-сервер может отвечать на запросы по одному т. е. через 1 секунду каждый.

Теперь, когда выполняется 1-й запрос, служба-исполнительвыбирает другой запрос и запускает его, и это продолжается до последнего запроса.

4 одновременно блокируются на сервере HTTP, которые обслуживаются последовательно один за другим.

Чтобы получить Proof of Concept этой теории, вы можете использовать службу обмена сообщениями (очередь), которая может одновременно получать тест по 4 каналам.Это должно сократить время.

...