fix(resource): JsParserExecutor WorkerExecutor 懒加载 + 关闭支持

原代码静态初始化 WorkerExecutor,应用关闭时无法释放线程资源。

改为:
- 懒加载创建 WorkerExecutor
- 实现 AutoCloseable 接口
- 添加 shutdownExecutor() 静态方法供应用关闭时调用
This commit is contained in:
yukaidi
2026-05-29 00:32:13 +08:00
parent be1ed3d46d
commit 8dfcf510f6

View File

@@ -29,12 +29,13 @@ import java.util.stream.Collectors;
* @author <a href="https://qaiu.top">QAIU</a> * @author <a href="https://qaiu.top">QAIU</a>
* Create at 2025/10/17 * Create at 2025/10/17
*/ */
public class JsParserExecutor implements IPanTool { public class JsParserExecutor implements IPanTool, AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(JsParserExecutor.class); private static final Logger log = LoggerFactory.getLogger(JsParserExecutor.class);
private static final WorkerExecutor EXECUTOR = WebClientVertxInit.get().createSharedWorkerExecutor("parser-executor", 32); private static WorkerExecutor EXECUTOR;
private static final Object EXECUTOR_LOCK = new Object();
private static String FETCH_RUNTIME_JS = null; private static String FETCH_RUNTIME_JS = null;
private final CustomParserConfig config; private final CustomParserConfig config;
@@ -149,6 +150,7 @@ public class JsParserExecutor implements IPanTool {
/** /**
* 释放资源ScriptEngine 和 HttpClient避免内存泄漏 * 释放资源ScriptEngine 和 HttpClient避免内存泄漏
*/ */
@Override
public void close() { public void close() {
if (httpClient != null) { if (httpClient != null) {
httpClient.close(); httpClient.close();
@@ -162,12 +164,40 @@ public class JsParserExecutor implements IPanTool {
} }
} }
/**
* 关闭全局 WorkerExecutor应在应用关闭时调用
*/
public static void shutdownExecutor() {
synchronized (EXECUTOR_LOCK) {
if (EXECUTOR != null) {
EXECUTOR.close();
EXECUTOR = null;
log.info("JsParserExecutor WorkerExecutor 已关闭");
}
}
}
/**
* 获取或创建 WorkerExecutor懒加载
*/
private static WorkerExecutor getExecutor() {
if (EXECUTOR != null) {
return EXECUTOR;
}
synchronized (EXECUTOR_LOCK) {
if (EXECUTOR == null) {
EXECUTOR = WebClientVertxInit.get().createSharedWorkerExecutor("parser-executor", 32);
}
return EXECUTOR;
}
}
@Override @Override
public Future<String> parse() { public Future<String> parse() {
jsLogger.info("开始执行JavaScript解析器: {}", config.getType()); jsLogger.info("开始执行JavaScript解析器: {}", config.getType());
// 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程 // 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程
return EXECUTOR.executeBlocking(() -> { return getExecutor().executeBlocking(() -> {
// 直接调用全局parse函数 // 直接调用全局parse函数
Object parseFunction = engine.get("parse"); Object parseFunction = engine.get("parse");
if (parseFunction == null) { if (parseFunction == null) {
@@ -197,7 +227,7 @@ public class JsParserExecutor implements IPanTool {
jsLogger.info("开始执行JavaScript文件列表解析: {}", config.getType()); jsLogger.info("开始执行JavaScript文件列表解析: {}", config.getType());
// 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程 // 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程
return EXECUTOR.executeBlocking(() -> { return getExecutor().executeBlocking(() -> {
// 直接调用全局parseFileList函数 // 直接调用全局parseFileList函数
Object parseFileListFunction = engine.get("parseFileList"); Object parseFileListFunction = engine.get("parseFileList");
if (parseFileListFunction == null) { if (parseFileListFunction == null) {
@@ -230,7 +260,7 @@ public class JsParserExecutor implements IPanTool {
jsLogger.info("开始执行JavaScript按ID解析: {}", config.getType()); jsLogger.info("开始执行JavaScript按ID解析: {}", config.getType());
// 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程 // 使用executeBlocking在工作线程上执行避免阻塞EventLoop线程
return EXECUTOR.executeBlocking(() -> { return getExecutor().executeBlocking(() -> {
// 直接调用全局parseById函数 // 直接调用全局parseById函数
Object parseByIdFunction = engine.get("parseById"); Object parseByIdFunction = engine.get("parseById");
if (parseByIdFunction == null) { if (parseByIdFunction == null) {