我使用老旧的平台线程构建了一个简单的网络爬虫。它只是一个多线程爬虫,没什么特别的。但是,我突然好奇:“如果我改用虚拟线程会怎么样?”
虚拟线程是我最喜欢的 Java 生态系统新特性之一。从平台线程切换到虚拟线程后,URL 处理速度显著提升……直到后来,一切都崩溃了 OutOfMemoryError。
是的。快得太快了。
我喜欢探索 Java 的新功能。虚拟线程、记录、模式匹配等等,不一而足。但每种工具都有其优缺点。在软件工程中,解决一个问题可能会引发另一个问题。我们的工作不仅仅是编写代码,还要平衡性能、安全性和可维护性。
这篇文章是关于我的小小黑客经验的,我尝试释放虚拟线程的威力,却发现需要一些技巧才能避免性能问题变成内存炸弹。我会带你了解发生了什么,我是如何修复的,以及你也能做到的。
1
网络爬虫:线程版本
在使用虚拟线程之前,我使用传统的平台线程创建了一个非常基础的爬虫。需要处理的 URL 列表是预先确定的,并插入到执行器队列中。每个平台线程都会从队列中获取一个新任务,该任务包括:获取 URL,从本地服务器获取其内容(以消除带宽和外部速率限制),模拟一些处理过程,然后继续执行下一个任务。
public class PlatformThreadsCrawler {
//用于获取和处理数据的执行器服务 private final ExecutorService executorService = Executors.newFixedThreadPool( 200 ); private final HttpClient httpClient = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds( 10 )) .build(); private final AtomicLong processedCount = new AtomicLong ( 0 );
//提交执行器队列中的作业,然后等待结果 public void crawlUrls (List<String> urls) { long startTime = System.currentTimeMillis();
// 提交所有下载任务 CompletableFuture<?>[] futur