我正在尝试使用 java 程序在 2 分钟内处理一个 4000 行的文件。我无法在这里发布代码,但我会尽力描述它。
每行的处理涉及 2 组 HTTP 请求,每组包含 2 个顺序/相关的 HTTP 请求。每个请求大约需要 300 – 500 毫秒。我需要比较两组的响应。
如上所述,这些集合是相互独立的。因此,我决定将它们中的每一个作为池中的两个单独的线程启动:internalExecutorService。我们将此过程称为 LINE_PROCSSOR。
ExecutorService internalExecutorService = Executors.newFixedThreadPool(90);
我需要处理多条这样的线路。因此,每一行都由不同的线程处理。第二个线程池(lineExecutorService)为此提供线程。我们将此进程称为 LINE_LIST_PROCESSOR
ExecutorService lineExecutorService = Executors.newFixedThreadPool(40);
为了处理 4000 行这样的行,我决定将它们分成每行 500 行的块。然后每个块在单独的线程中处理。为此创建了一个新的线程池。我将其称为 chunkExecutorService。
ExecutorService chunkExecutorService = Executors.newFixedThreadPool(7);
池大小的选择基于,
chunkExecutorService = 7。我使用8核的CPU。于是决定去 7.1 用于主线程。 lineExecutorService = 40. 随机选择 基于各种运行。我最终遇到了更多线程的堆栈错误。 InternalExecutorService = 90. 只是上面的两倍,还有一些 缓冲线程,因为每行需要 2 个线程。
我使用上述设置进行了 POC。它在本地确实有效。但我完全不相信我的设计方式,并且我相信我的线程池大小选择没有任何意义。当部署到服务器时,它可能无法正常工作。 [注意:我没有选择 java 并行流,因为我觉得 ExecutorService 可以更好地处理线程池]。 我没有广泛使用线程,所以我正在寻求您的专家建议来评估我的设计并建议是否有更好的方法来处理它。
如果您需要四个不同的图表来传达迭代文件并每行执行两个 http 请求的解决方案,您的解决方案可能会过于复杂......
与其尝试修剪多余的复杂性,不如从另一个方向考虑:从最简单的实现开始,然后使其变得更加复杂,直到满足性能目标。
最简单的解决方案是这样的:
for (var line : lines) {
var data = parse(line);
var firstResponse = firstCall(data);
var secondResponse = secondCall(data);
var c = compare(firstResponse, secondResponse);
var result = ...;
}
该解决方案的执行时间为 4000 * (0.5 + 0.5) = 4000 秒。最大的乘数是 4000,所以让我们解决这个问题:
var executor = ...;
var futures = new ArrayList<Future<Result>>();
for (var line : lines) {
var data = parse(line);
futures.add(executor.submit(() -> {
var firstResponse = firstCall(data);
var secondResponse = secondCall(data);
var c = compare(firstResponse, secondResponse);
return ...;
}));
}
for (var future : futures) {
var result = future.get();
// do something with result
}
如果您有足够的线程来并行发送 4000 个请求,则此代码的执行时间为 (0.5 + 0.5) 秒。实际上,这个限制因素将是另一个系统。
所以,我们就完成了。不需要在单独的线程中解析数据,因为解析速度很快。无需在单独的线程中发送两个请求,因为它只为我们赢得了半秒的时间。