con*_*ter 5 java asynchronous spring-boot
I am working on an application which needs to test 1000's of proxy servers continuously. The application is based around Spring Boot.
The current approach I am using is @Async decorated method which takes a proxy server and returns the result.
I am often getting OutOfMemory error and the processing is very slow. I assume that is because each async method is executed in a separate thread which blocks on I/O?
Everywhere I read about async in Java, people mix parallel execution in threads with non-blocking IO. In the Python world, there is the async library which executes I/O requests in a single thread. While a method is waiting for a response from server, it starts executing other method.
我认为就我而言,我需要这样的东西,因为 Spring 的 @Async 不适合我。有人可以帮助消除我的困惑并建议我应该如何应对这个挑战吗?
我想同时检查 100 个代理而不增加过多的负载。我已经阅读了 Apache Async HTTP Client,但我不知道它是否合适?
这是我正在使用的线程池配置:
public Executor proxyTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2 - 1);
executor.setMaxPoolSize(100);
executor.setDaemon(true);
return executor;
}
Run Code Online (Sandbox Code Playgroud)
\n\n\n我经常遇到 OutOfMemory 错误,并且处理速度非常慢。\n 我认为这是因为每个异步方法都在一个单独的\n 线程中执行,该线程会阻塞 I/O?
\n
对于OOME,我在第二点中解释。
\n关于缓慢,确实与请求/响应处理中执行的I/O有关。
\n问题来自有效并行运行的线程数量。
\n根据您的实际配置,永远不会达到池最大数量(我在下面解释原因)。\n假设corePoolSize==10在你的情况下。这意味着10个线程并行运行。假设每个线程运行大约 3 秒来测试站点。
\n这意味着您在大约 0.3 秒内测试一个站点。测试 1000 个站点需要 300 秒。
\n它足够慢,并且时间的一个重要部分是等待时间:从当前测试的站点发送/接收请求/响应的 I/O。
\n为了提高整体速度,您最初应该并行运行比核心容量多得多的线程。通过这种方式,I/O 等待时间将不再是一个问题,因为线程之间的调度会很频繁,因此在线程暂停时,您会进行一些对线程没有价值的 I/O 处理。
它应该可以处理 OOME 问题,并且可能会大大提高执行时间,但不能保证您得到的时间很短。
\n为了实现它,您可能应该更精细地处理多线程逻辑,并依赖具有非阻塞 IO 的 API/库。
官方文档的一些信息应该会有帮助。
\n这部分解释了提交任务时的整体逻辑(重点是我的):
\n\n\n线程池的配置还应该根据执行器xe2x80x99s队列容量来考虑。有关池大小和队列容量之间关系的完整说明,请参阅 ThreadPoolExecutor 的文档。主要思想是,当提交任务时,如果当前活动线程数小于核心大小,则执行器首先尝试使用空闲线程。如果已达到核心大小,只要尚未达到其容量,任务就会添加到队列中。只有这样,如果已达到队列 xe2x80x99 的容量,执行器才会创建超出核心大小的新线程。如果也达到了最大大小,则执行程序将拒绝该任务。
\n
这解释了对队列大小的影响(重点仍然是我的):
\n\n\n\n\n默认情况下,队列是无界的,但这很少是所需的\n 配置,因为如果在所有池线程都忙时向该队列添加足够的\n 任务,则可能会导致 OutOfMemoryErrors。 \n 此外,如果队列是无界的,最大大小根本没有影响。由于执行器总是在创建超出核心大小的新线程之前尝试队列,因此队列必须具有有限的容量,\n 线程池才能增长到超出核心大小(这就是为什么固定大小的池是使用无界队列时唯一明智的情况)。
\n
长话短说:您没有设置默认情况下无界的队列大小 ( Integer.MAX_VALUE)。因此,您在队列中填充了数百个任务,这些任务只会在很晚之后才会弹出。这些任务使用大量内存,而OOME上升。
此外,正如文档中所解释的,此设置对于无界队列无济于事,因为只有当队列已满时才会创建新线程:
\n\nexecutor.setMaxPoolSize(100);\nRun Code Online (Sandbox Code Playgroud)\n\n将这两个信息设置为相关值更有意义:
\n\npublic Executor proxyTaskExecutor() {\n ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2 - 1);\n executor.setMaxPoolSize(100);\n executor.setQueueCapacity(100); \n executor.setDaemon(true);\n return executor;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n或者作为替代方案,使用初始和最大池大小具有相同值的固定大小池:
\n\n\n\n\n执行程序\xe2\x80\x99s 线程池不仅可以具有单一大小,还可以具有不同的核心值和最大大小。如果您提供单个值,则执行程序具有固定大小的线程池(核心大小和最大大小相同)。
\n
public Executor proxyTaskExecutor() {\n ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n executor.setCorePoolSize(100);\n executor.setMaxPoolSize(100);\n executor.setDaemon(true);\n return executor;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n另请注意,在不暂停的情况下调用 1000 次异步服务似乎对内存有害,因为它无法直接处理它们。您可能应该通过在它们之间执行 thread.sleep() 将这些调用分成更小的部分(2、3 或更多)。
\n| 归档时间: |
|
| 查看次数: |
3279 次 |
| 最近记录: |