
oracle 圣何塞 debian arm 4C 24G
JDK 21, 不能在本地上复现 代理程序在 服务器上
htop 观察到 是某一个核心突然满载 然后就卡住了
public class Main { static Log log = Log.get(); static final Gson gson = new Gson(); static List<Image> images = null; static int maxThreads = 100; // 控制固定线程池的大小 static ExecutorService virtualThreadPool = Executors.newFixedThreadPool(maxThreads, Thread.ofVirtual().factory()); static String filePath = "/root/java_work/imglist/"; // 失败 list static List<String> failList = Lists.newArrayList(); public static void main(String[] args) { OkHttpUtils.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 6789))); List<String> types = List.of(".jpg", ".png"); try { images = gson.fromJson(new FileReader("output-2024-4-16.json"), new TypeToken<List<Image>>(){}.getType()); } catch (IOException e) { log.info("读取文件失败: {}", e.getMessage()); return; } log.info("加载到的图片数量: {}", images.size()); images.forEach(image -> virtualThreadPool.submit(() -> { for (String type : types) { String url = convertPreviewToImageUrl(image.getHref(), type); if (attemptToDownloadImage(url,0)) { image.setSourceUrl(url); // 更新 Image 对象 break; } } })); virtualThreadPool.shutdown(); while (!virtualThreadPool.isTerminated()) { Thread.onSpinWait(); } // 将更新后的 images 列表写回到 JSON 文件 writeImagesToJson(images, "output-2024-4-16-ok.json"); writeImagesToJson(failList, "output-2024-4-16-fail.json"); } private static boolean attemptToDownloadImage(String url, int retryCount) { if (retryCount >= 3) { log.info("重试次数过多,放弃下载: {}", url); failList.add(url); return false; } try (Response respOnse= OkHttpUtils.get(url, Headers.of("Connection", "close"))) { switch (response.code()) { case 200: log.info("成功下载图片: {}", url); byte[] bytes = Objects.requireNonNull(response.body()).bytes(); writeImageToFile(bytes, url.substring(url.lastIndexOf('/') + 1)); return true; case 404: log.info("图片不存在: {}", url); return false; case 429: log.info("请求过于频繁,需要稍后重试: {}", url); handleRateLimiting(); return attemptToDownloadImage(url, retryCount + 1); default: log.info("其他 HTTP 响应: {}", response.code()); return false; } } catch (Exception e) { log.error("请求图片时出错: {}", e.fillInStackTrace()); handleRateLimiting(); return attemptToDownloadImage(url, retryCount + 1); } } private static void handleRateLimiting() { try { log.info("等待 5 秒后重试"); Thread.sleep(5000); // 延迟 5 秒后重试 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static String convertPreviewToImageUrl(String previewUrl, String type) { String id = previewUrl.substring(previewUrl.lastIndexOf('/') + 1); return String.format("https://w.wallhaven.cc/full/%s/wallhaven-%s%s", id.substring(0, 2), id, type); } public static void writeImagesToJson(List<?> objects,String fileName) { try (FileWriter writer = new FileWriter(fileName)) { gson.toJson(objects, writer); } catch (IOException e) { log.error("写入文件时出错: {}", e.getMessage()); } } public static void writeImageToFile(byte[] bytes, String fileName) { try (FileOutputStream fos = new FileOutputStream(filePath+fileName)) { fos.write(bytes); log.info("成功写入文件: {}", fileName); } catch (IOException e) { log.error("写入文件时出错: {}", e.getMessage()); } } } https://gist.github.com/dnslin/fe657f9df08f4286c197c5e9e5fd6a51
1 blankmiss OP 我尝试削减过 线程池的数量 减到了 6 个 都会卡住 |
2 blankmiss OP 但是我改成线程池就不会出现这种情况 |
3 sagaxu 2024-04-16 15:23:44 +08:00 1. 很多第三方库甚至 JDK 库尚不支持虚拟线程 2. 用 JFR 记录观察是哪个地方卡住了 3. onSpinWait 换成 Thread.sleep 试试,busy-waiting 不宜等待太久 |
4 blankmiss OP @sagaxu 意思是在某些情况下 虚拟线程会被阻塞掉? 第三点我去试试 我现在已经在用 arthas-boot.jar 分析了 |
5 zizon 2024-04-16 22:27:40 +08:00 failList 换线程安全的看看? |