У меня есть сервлет, который обрабатывает запросы пользователей, когда пользователь отправляет запрос, который мне нужно собрать. данные со многих конечных точек. Один пользователь может иметь данные из многих источников, поэтому мне нужно получить все данные из всех источников и ответить на них пользователю. Чтобы получить данные из одной конечной точки, мне нужно вызвать URL-адрес HTTP. Это занимает значительное количество времени. Допустим, от 1 секунды до 20 минут.
Итак, сейчас, например, у нас есть пользователь Jonh и у него 3 источника с документами. Он запрашивает свои документы у моего сервлета, и я начинаю собирать документы. В зависимости от источника я запрашиваю данные и собираю их в список, а затем отвечаю Джону. Очевидно, Джон может подождать некоторое время и разозлится. Вот пример:
Код: Выделить всё
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
public class SearchDocumentsController {
@GetMapping("/search/{userId}")
public List searchDocuments(@PathVariable String userId) {
List connections = getUserConnections(userId);
List documents = syncDocuments(userId, connections);
return documents;
}
// Simulate a method that retrieves user connections
private List getUserConnections(String userId) {
return List.of("one", "two", "three");
}
// This method is the one that takes a long time to execute
public List syncDocuments(String userId, List connections) {
List documents = new ArrayList();
for (String connection : connections) {
List documentsFromOneConnection = searchInDataSource(userId, connection);
documents.addAll(documentsFromOneConnection);
}
return documents;
}
// Simulate a slow data source
private List searchInDataSource(String userId, String connection) {
List documents = new ArrayList();
for (int i = 0; i < 3; i++) {
sleepSeconds(3);
documents.add("doc " + i + " from " + connection + " for " + userId);
}
return documents;
}
private void sleepSeconds(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Код: Выделить всё
@GetMapping("/search/{userId}")
public List searchDocuments(@PathVariable String userId) {
long start = System.currentTimeMillis();
List connections = getUserConnections(userId);
List documents = asyncDocuments(userId, connections);
long end = System.currentTimeMillis();
System.out.println("Time taken: " + (end - start) / 1000 + " seconds");
return documents;
}
// This method is the one that takes a long time to execute
public List asyncDocuments(String userId, List connections) {
List futures = new ArrayList();
for (String connection : connections) {
futures.add(CompletableFuture.supplyAsync(() -> searchInDataSource(userId, connection)));
}
// Wait for all futures to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();//block until all futures are completed
//By tis point all futures are completed
List collectedResults = new ArrayList();
for (CompletableFuture future : futures) {
collectedResults.addAll(future.join()); //join() will return the result of the future without blocking
}
return collectedResults;
}
ВОПРОС: Можно ли создать набор потоков из сервлета? Есть ли лучший способ получить тот же результат? Основная проблема – ресурсы. Я использую глобальный пул потоков, поэтому есть граница, у меня есть тайм-аут, после которого мне нужно вернуть все документы, которые я нашел в этот момент, но в любом случае это выглядит неправильно. Я читал на практике о параллелизме Java, что это неправильно (я не помню главу), я видел несколько ТАК вопросов, что это не очень хороший подход, но я ищу хорошее объяснение, ссылки на книгу, статью и т. д. а также, если кто-то знает альтернативный шаблон/подход/архитектуру для решения этой задачи, я бы это услышал и оценил бы.
Подробнее здесь: https://stackoverflow.com/questions/787 ... et-and-wha