Сейчас я использую для этого Java CompletableFuture, но по мере роста количества ссылок медленнее становятся запросы.
Вот HttpClient:
Код: Выделить всё
import java.net.http.HttpClient;
private final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
@Override
public CompletableFuture getPageContentAsync(String url) {
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.GET()
.build();
return this.client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(resp -> resp.statusCode() == 200 ? resp.body() : "");
}
Код: Выделить всё
public class SearchTask implements Runnable {
private final String id;
private final String keyword;
// Repository is just a ConcurrentHashMap.
private final SearchRepository repository;
private final ExecutorService crawlPool;
private final Set urlsVisited = ConcurrentHashMap.newKeySet();
public SearchTask(String id, String keyword,
SearchRepository repository, ExecutorService crawlPool) {
this.id = id;
this.keyword = keyword;
this.repository = repository;
this.crawlPool = crawlPool;
}
@Override
public void run() {
try {
this.crawlAsync("https://www.kernel.org/pub/linux/docs/man-pages/").join();
// sysout("task with id %s finished")
} finally {
this.crawlPool.shutdown();
}
}
private CompletableFuture crawlAsync(String url) {
if (this.urlsVisited.add(url))
return this.httpService.getPageContentAsync(url, this.id)
.thenComposeAsync(html -> {
if (HtmlUtils.containsKeyword(html, this.keyword)) this.repository.addUrl(this.id, url);
// The utils will get all the href tags and extract the text.
Set links = HtmlUtils.extractLinksFromHtml(html)
.stream()
.filter(link -> !this.urlsVisited.contains(link))
.collect(Collectors.toSet());
CompletableFuture[] futures = links.stream().limit(2)
.map(this::crawlAsync)
.toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(futures);
}, this.crawlPool);
return CompletableFuture.completedFuture(null);
}
}
Код: Выделить всё
int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
Я также пытался вставить семафор, но он не работал должным образом, казалось, что запросы останавливались до того, как все страницы были просканированы.
У меня есть скриншот профилирования, который может помочь понять, что происходит.

Ожидаемый результат заключается в том, что приложение сможет сканировать веб-сайт с несколькими поисками одновременно, поэтому объем ресурсов для каждого запроса может быть минимальным.
Подробнее здесь: https://stackoverflow.com/questions/797 ... throttling