首页 > 解决方案 > 并行化数千次下载的最佳方式

问题描述

我正在创建一个应用程序,我必须在其中使用 Java 下载数千张图像(每张约 1 MB)。

我在我的 REST 请求中获取相册 URL 列表,每个相册包含多个图像。

所以我的请求看起来像:

[
  "www.abc.xyz/album1",
  "www.abc.xyz/album2",
  "www.abc.xyz/album3",
  "www.abc.xyz/album4",
  "www.abc.xyz/album5"
]

假设每个相册有 1000 张图片,那么我需要并行下载 50000 张图片。

现在我已经实现了它,parallelStream()但我觉得我可以进一步优化它。

有两个主要类 -AlbumDownloaderImageDownloader(Spring 组件)。

所以主应用程序parallelStream()在专辑列表上创建了一个:

albumData.parallelStream().forEach(ad -> albumDownloader.downloadAlbum(ad));

在 AlbumDownloader -> downloadAlbum() 方法中还有一个 parallelStream():

List<Boolean> downloadStatus = albumData.getImageDownloadData().parallelStream().map(idd -> imageDownloader.downloadImage(idd)).collect(Collectors.toList());

我正在考虑使用CompletableFuturewithExecutorService但我不确定我应该使用什么池大小?

我应该为每个专辑创建一个单独的池吗?

ExecutorService executor = Executors.newFixedThreadPool(Math.min(albumData.getImageDownloadData().size(), 1000));

这将创建 5 个不同的池,每个池有 1000 个线程,这就像 5000 个线程可能会降低性能而不是提高性能。

你能给我一些想法让它变得非常快吗?

顺便说一句,我正在使用 Apache Commons IOFileUtils下载文件,并且我有一台具有 12 个可用 CPU 内核的机器。

标签: javamultithreadingjava-streamcompletable-futurefileutils

解决方案


使其“非常非常快”的唯一方法是获得与服务器的“非常非常快”的网络连接;例如,将您的客户端与您正在下载的服务器放在一起。

您的下载速度将受到许多潜在瓶颈的限制。这些包括:

  1. 服务器的性能;即它可以以多快的速度组装数据以发送给您并通过其网络接口推送它。

  2. 服务施加的每用户请求限制。

  3. 客户端和服务器之间的网络路径的端到端性能。

  4. 您正在运行的机器在从网络移动数据并将其(我猜)放到本地磁盘方面的性能。

瓶颈可能是这些中的任何一个,或它们的组合。

在问题上投入数千个线程不太可能改善问题。事实上,如果有的话,它可能会使性能不太理想。例如:

  • 它可能会阻塞您的网络链接,或者
  • 它可能会在您从中获取的服务器中触发反占用或反 DOS 防御。

一个更好的主意是使用带有小型有界工作池的 ExecutorService,并将下载作为任务提交到池中。(并尝试在下载之间保持 HTTP / HTTPS 连接打开。)


我还建议您确保您有权做您正在做的事情。音乐出版行业的公司拥有优秀的律师。如果他们认为您违反了他们的条款和条件或窃取了他们的知识产权,他们可能会让您的生活变得不愉快1 。

1 - 例如阻止您的 IP 地址或向您的服务提供商发出删除请求。


推荐阅读