mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-10-13 02:04:25 +00:00
fix(web): harden cache and API services
This commit is contained in:
@@ -3,9 +3,11 @@ package cn.qaiu.lz;
|
||||
import cn.qaiu.WebClientVertxInit;
|
||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
||||
import cn.qaiu.lz.common.cache.CacheManager;
|
||||
import cn.qaiu.lz.common.interceptorImpl.RateLimiter;
|
||||
import cn.qaiu.lz.web.config.PlaygroundConfig;
|
||||
import cn.qaiu.lz.web.service.DbService;
|
||||
import cn.qaiu.lz.web.service.impl.ShoutServiceImpl;
|
||||
import cn.qaiu.parser.custom.CustomParserConfig;
|
||||
import cn.qaiu.parser.custom.CustomParserRegistry;
|
||||
import cn.qaiu.parser.customjs.JsScriptMetadataParser;
|
||||
@@ -20,6 +22,7 @@ import io.vertx.core.json.jackson.DatabindCodec;
|
||||
import io.vertx.core.shareddata.LocalMap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -38,22 +41,45 @@ import static cn.qaiu.vx.core.util.ConfigConstant.LOCAL;
|
||||
public class AppMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 先注册 ShutdownHook(JVM 逆序执行,先注册的后执行)
|
||||
// 确保关闭顺序:Vert.x -> JDBCPoolInit -> JsParserExecutor
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
JDBCPoolInit.instance().close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
cn.qaiu.parser.customjs.JsParserExecutor.shutdownExecutor();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}));
|
||||
applyRuntimeLogLevelOverride();
|
||||
Deploy deploy = Deploy.instance();
|
||||
// 先阻断应用级定时任务,再让 Vert.x 停入口和 verticle。
|
||||
deploy.addPreShutdownTask(CacheManager::cancelPeriodicCleanup);
|
||||
deploy.addPreShutdownTask(ShoutServiceImpl::cancelCleanup);
|
||||
// Vert.x 停完后再关数据库和解析器共享资源,避免请求还在路上就先关底层 client。
|
||||
deploy.addPostShutdownTask(() -> JDBCPoolInit.instance().close());
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsParserExecutor::shutdownExecutor);
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsPlaygroundExecutor::shutdownPools);
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsHttpClient::shutdownSharedClient);
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.PanBase::shutdownSharedClients);
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.IPanTool::shutdownCloseAfterScheduler);
|
||||
deploy.addPostShutdownTask(cn.qaiu.parser.impl.PodTool::shutdownWorkerExecutor);
|
||||
// start
|
||||
Deploy.instance().start(args, AppMain::exec);
|
||||
deploy.start(args, AppMain::exec);
|
||||
}
|
||||
|
||||
private static void applyRuntimeLogLevelOverride() {
|
||||
String levelName = System.getProperty("NFD_LOG_LEVEL");
|
||||
if (levelName == null || levelName.isBlank()) {
|
||||
levelName = System.getenv("NFD_LOG_LEVEL");
|
||||
}
|
||||
if (levelName == null || levelName.isBlank()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var level = ch.qos.logback.classic.Level.toLevel(levelName, null);
|
||||
if (level == null) {
|
||||
log.warn("忽略无效的 NFD_LOG_LEVEL: {}", levelName);
|
||||
return;
|
||||
}
|
||||
var logger = LoggerFactory.getLogger("cn.qaiu");
|
||||
if (logger instanceof ch.qos.logback.classic.Logger logbackLogger) {
|
||||
logbackLogger.setLevel(level);
|
||||
log.info("cn.qaiu 日志级别已覆盖为 {}", level);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("覆盖 cn.qaiu 日志级别失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,6 +91,8 @@ public class AppMain {
|
||||
private static void exec(JsonObject jsonObject) {
|
||||
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
||||
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
||||
// 演练场配置要先加载,后续启动流程才能按开关决定是否注册动态解析器。
|
||||
PlaygroundConfig.loadFromJson(jsonObject);
|
||||
// 限流
|
||||
if (jsonObject.containsKey("rateLimit")) {
|
||||
JsonObject rateLimit = jsonObject.getJsonObject("rateLimit");
|
||||
@@ -108,7 +136,12 @@ public class AppMain {
|
||||
} catch (Exception e) {
|
||||
log.warn("读取代理配置失败,使用默认页面地址: {}", e.getMessage());
|
||||
}
|
||||
loadPlaygroundParsers(pageAddr);
|
||||
if (PlaygroundConfig.getInstance().isEnabled()) {
|
||||
loadPlaygroundParsers(pageAddr);
|
||||
} else {
|
||||
log.info("演练场功能已禁用,跳过加载演练场解析器");
|
||||
log.info("服务已启动,可通过 {} 访问页面", pageAddr);
|
||||
}
|
||||
System.out.println("启动成功: \n本地服务地址: " + addr);
|
||||
});
|
||||
});
|
||||
@@ -142,9 +175,6 @@ public class AppMain {
|
||||
JsonObject auths = jsonObject.getJsonObject(ConfigConstant.AUTHS);
|
||||
localMap.put(ConfigConstant.AUTHS, auths);
|
||||
}
|
||||
|
||||
// 演练场配置
|
||||
PlaygroundConfig.loadFromJson(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,34 +183,31 @@ public class AppMain {
|
||||
private static void loadPlaygroundParsers(String accessAddr) {
|
||||
DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
|
||||
|
||||
dbService.getPlaygroundParserList().onSuccess(result -> {
|
||||
dbService.getEnabledPlaygroundParsersForLoad().onSuccess(result -> {
|
||||
JsonArray parsers = result.getJsonArray("data");
|
||||
if (parsers != null) {
|
||||
int loadedCount = 0;
|
||||
for (int i = 0; i < parsers.size(); i++) {
|
||||
JsonObject parser = parsers.getJsonObject(i);
|
||||
|
||||
// 只注册已启用的解析器
|
||||
if (parser.getBoolean("enabled", false)) {
|
||||
try {
|
||||
String jsCode = parser.getString("jsCode");
|
||||
if (jsCode == null || jsCode.trim().isEmpty()) {
|
||||
log.error("加载演练场解析器失败: {} - JavaScript代码为空", parser.getString("name"));
|
||||
continue;
|
||||
}
|
||||
CustomParserConfig config = JsScriptMetadataParser.parseScript(jsCode);
|
||||
CustomParserRegistry.register(config);
|
||||
loadedCount++;
|
||||
log.info("已加载演练场解析器: {} ({})",
|
||||
config.getDisplayName(), config.getType());
|
||||
} catch (Exception e) {
|
||||
String parserName = parser.getString("name");
|
||||
String errorMsg = e.getMessage();
|
||||
log.error("加载演练场解析器失败: {} - {}", parserName, errorMsg, e);
|
||||
// 如果是require相关错误,提供更详细的提示
|
||||
if (errorMsg != null && errorMsg.contains("require")) {
|
||||
log.error("提示:演练场解析器不支持CommonJS模块系统(require),请确保代码使用ES5.1语法");
|
||||
}
|
||||
try {
|
||||
String jsCode = parser.getString("jsCode");
|
||||
if (jsCode == null || jsCode.trim().isEmpty()) {
|
||||
log.error("加载演练场解析器失败: {} - JavaScript代码为空", parser.getString("name"));
|
||||
continue;
|
||||
}
|
||||
CustomParserConfig config = JsScriptMetadataParser.parseScript(jsCode);
|
||||
CustomParserRegistry.register(config);
|
||||
loadedCount++;
|
||||
log.info("已加载演练场解析器: {} ({})",
|
||||
config.getDisplayName(), config.getType());
|
||||
} catch (Exception e) {
|
||||
String parserName = parser.getString("name");
|
||||
String errorMsg = e.getMessage();
|
||||
log.error("加载演练场解析器失败: {} - {}", parserName, errorMsg, e);
|
||||
// 如果是require相关错误,提供更详细的提示
|
||||
if (errorMsg != null && errorMsg.contains("require")) {
|
||||
log.error("提示:演练场解析器不支持CommonJS模块系统(require),请确保代码使用ES5.1语法");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user