perf: 优化插件的刷新加载机制

This commit is contained in:
藏柏 2025-03-18 14:45:55 +08:00
parent b455aa58fc
commit aef596ada2
9 changed files with 2014 additions and 1327 deletions

View File

@ -1,9 +1,7 @@
package com.jetbrains.help;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jetbrains.help.context.*;
import lombok.SneakyThrows;
@ -13,14 +11,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.*;
import java.net.InetAddress;
import java.util.Collection;
import java.util.List;
@Slf4j
@Slf4j(topic = "源项目入口")
@EnableScheduling
@Import(SpringUtil.class)
@SpringBootApplication
@ -41,15 +36,15 @@ public class JetbrainsHelpApplication {
InetAddress localHost = InetAddress.getLocalHost();
String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port"));
String runSuccessWarn = "\n====================================================================================\n" +
"= Jetbrains-Help Run Success~ =\n" +
"= address:" + address + " =\n" +
"= Jetbrains-Help 启动成功~ =\n" +
"= 访问地址:" + address + " =\n" +
"====================================================================================\n";
log.info(runSuccessWarn);
}
@Scheduled(cron = "0 0 12 * * ?")
public void refresh() {
PluginsContextHolder.refreshJsonFile();
ThreadUtil.execute(PluginsContextHolder::refreshJsonFile);
}
}

View File

@ -20,7 +20,7 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Slf4j(topic = "代理上下文")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AgentContextHolder {
@ -33,18 +33,18 @@ public class AgentContextHolder {
private static File jaNetfilterZipFile;
public static void init() {
log.info("Agent context init loading...");
log.info("初始化中...");
jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip");
if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) {
unzipJaNetfilter();
if (!powerConfHasInit()) {
log.info("Agent config init loading...");
log.info("配置初始化中...");
loadPowerConf();
zipJaNetfilter();
log.info("Agent config init success !");
log.info("配置初始化成功!");
}
}
log.info("Agent context init success !");
log.info("初始化成功!");
}
public static File jaNetfilterZipFile() {
@ -57,7 +57,7 @@ public class AgentContextHolder {
try {
powerConfStr = IoUtil.readUtf8(FileUtil.getInputStream(powerConfFile));
} catch (IORuntimeException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File read failed", POWER_CONF_FILE_NAME), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件读取失败!", POWER_CONF_FILE_NAME), e);
}
return CharSequenceUtil.containsAll(powerConfStr, "[Result]", "EQUAL,");
}
@ -68,7 +68,7 @@ public class AgentContextHolder {
.thenApply(AgentContextHolder::generatePowerConfigStr)
.thenAccept(AgentContextHolder::overridePowerConfFileContent)
.exceptionally(throwable -> {
log.error("agent context init or refresh failed", throwable);
log.error("配置初始化失败!", throwable);
return null;
}).join();
}
@ -94,7 +94,7 @@ public class AgentContextHolder {
try {
FileUtil.writeString(configStr, powerConfFile, StandardCharsets.UTF_8);
} catch (IORuntimeException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File write failed", POWER_CONF_FILE_NAME), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件写入失败!", POWER_CONF_FILE_NAME), e);
}
}

View File

@ -27,7 +27,7 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
@Slf4j
@Slf4j(topic = "证书上下文")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CertificateContextHolder {
@ -45,20 +45,20 @@ public class CertificateContextHolder {
private static File crtFile;
public static void init() {
log.info("certificate context init loading...");
log.info("初始化中...");
rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME);
if (!FileTools.fileExists(PRIVATE_KEY_FILE_NAME)
|| !FileTools.fileExists(PUBLIC_KEY_FILE_NAME)
|| !FileTools.fileExists(CET_FILE_NAME)) {
log.info("certificate context generate loading...");
log.info("证书生成中...");
generateCertificate();
log.info("certificate context generate success!");
log.info("证书生成成功!");
} else {
privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME);
publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME);
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
}
log.info("certificate context init success !");
log.info("初始化成功!");
}
@ -99,11 +99,11 @@ public class CertificateContextHolder {
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false));
} catch (OperatorCreationException e) {
throw new IllegalArgumentException("Certificate operator creation exception", e);
throw new IllegalArgumentException("证书运算符创建异常!", e);
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException("The certificate encoding exception", e);
throw new IllegalArgumentException("证书编码异常", e);
} catch (CertificateException e) {
throw new IllegalArgumentException("The certificate read exception", e);
throw new IllegalArgumentException("证书读取异常", e);
}
}

View File

@ -24,7 +24,7 @@ import java.util.Set;
import static cn.hutool.crypto.asymmetric.SignAlgorithm.SHA1withRSA;
@Slf4j
@Slf4j(topic = "授权上下文")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LicenseContextHolder {
@ -52,7 +52,7 @@ public class LicenseContextHolder {
try {
certBase64 = Base64.encode(certificate.getEncoded());
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException("Certificate extraction failed", e);
throw new IllegalArgumentException("证书编码异常", e);
}
return CharSequenceUtil.format("{}-{}-{}-{}", licenseId, licensePartBase64, signatureBase64, certBase64);
}

View File

@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.jetbrains.help.util.FileTools;
@ -16,10 +17,10 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Slf4j(topic = "插件上下文")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PluginsContextHolder {
@ -36,20 +37,22 @@ public class PluginsContextHolder {
private static File pluginsJsonFile;
public static void init() {
log.info("Plugin context init loading...");
log.info("初始化中...");
pluginsJsonFile = FileTools.getFileOrCreat(PLUGIN_JSON_FILE_NAME);
String pluginJsonArray;
try {
pluginJsonArray = IoUtil.readUtf8(FileUtil.getInputStream(pluginsJsonFile));
} catch (IORuntimeException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File read failed", PLUGIN_JSON_FILE_NAME), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件读取失败!", PLUGIN_JSON_FILE_NAME), e);
}
if (CharSequenceUtil.isBlank(pluginJsonArray) || !JSONUtil.isTypeJSON(pluginJsonArray)) {
pluginCacheList = new ArrayList<>();
refreshJsonFile();
} else {
pluginCacheList = JSONUtil.toList(pluginJsonArray, PluginCache.class);
log.info("Plugin context init success !");
log.info("初始化成功!");
refreshJsonFile();
}
}
@ -58,26 +61,28 @@ public class PluginsContextHolder {
}
public static void refreshJsonFile() {
log.info("Init or Refresh plugin context from 'JetBrains.com' loading...");
log.info("从'JetBrains.com'刷新中...");
CompletableFuture
.supplyAsync(PluginsContextHolder::pluginList)
.thenApply(PluginsContextHolder::pluginListFilter)
.thenApply(PluginsContextHolder::pluginConversion)
.thenAccept(PluginsContextHolder::overrideJsonFile)
.thenRun(() -> log.info("刷新成功!"))
.exceptionally(throwable -> {
log.error("Plugin context init or refresh failed", throwable);
log.error("刷新失败!", throwable);
return null;
});
log.info("Init or Refresh plugin context success !");
}
public static void overrideJsonFile(List<PluginCache> pluginCaches) {
log.info("源大小 => [{}], 新增大小 => [{}]", pluginCacheList.size(), pluginCaches.size());
pluginCacheList.addAll(pluginCaches);
String jsonStr = JSONUtil.toJsonStr(pluginCacheList);
try {
FileUtil.writeString(jsonStr, pluginsJsonFile, StandardCharsets.UTF_8);
FileUtil.writeString(JSONUtil.formatJsonStr(jsonStr), pluginsJsonFile, StandardCharsets.UTF_8);
log.info("Json文件已覆写!");
} catch (IORuntimeException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File write failed", PLUGIN_JSON_FILE_NAME), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件写入失败!", PLUGIN_JSON_FILE_NAME), e);
}
}
@ -87,49 +92,58 @@ public class PluginsContextHolder {
.thenFunction(response -> {
try (InputStream is = response.bodyStream()) {
if (!response.isOk()) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_LIST_URL, response));
throw new IllegalArgumentException(CharSequenceUtil.format("{} 请求失败! = {}", PLUGIN_LIST_URL, response));
}
return IoUtil.readObj(is, PluginList.class);
PluginList pluginList = JSONUtil.toBean(IoUtil.readUtf8(is), PluginList.class);
log.info("获取大小 => [{}]", pluginList.getTotal());
return pluginList;
} catch (IOException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} The request io read failed", PLUGIN_LIST_URL), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 请求IO读取失败!", PLUGIN_LIST_URL), e);
}
});
}
public static List<PluginList.Plugin> pluginListFilter(PluginList pluginList) {
return pluginList.getPlugins()
List<PluginList.Plugin> plugins = pluginList.getPlugins()
.stream()
.filter(plugin -> !PluginsContextHolder.pluginCacheList.contains(new PluginCache().setId(plugin.getId())))
.filter(plugin -> !CharSequenceUtil.equals(plugin.getPricingModel(), "FREE"))
.toList();
log.info("过滤后大小 => [{}]", plugins.size());
return plugins;
}
public static List<PluginCache> pluginConversion(List<PluginList.Plugin> pluginList) {
return pluginList
List<PluginCache> list = pluginList
.stream()
.parallel()
.map(plugin -> {
String productCode = pluginInfo(plugin.getId()).getPurchaseInfo().getProductCode();
String productCode = pluginInfo(plugin).getPurchaseInfo().getProductCode();
return new PluginCache()
.setId(plugin.getId())
.setProductCode(productCode)
.setName(plugin.getName())
.setPricingModel(plugin.getPricingModel())
.setIcon(PLUGIN_BASIC_URL + plugin.getIcon())
.setIcon(StrUtil.isNotBlank(plugin.getIcon()) ? PLUGIN_BASIC_URL + plugin.getIcon() : null)
;
})
.toList();
log.info("转换后大小 => [{}]", list.size());
return list;
}
public static PluginInfo pluginInfo(Long pluginId) {
return HttpUtil.createGet(PLUGIN_INFO_URL + pluginId)
public static PluginInfo pluginInfo(PluginList.Plugin plugin) {
return HttpUtil.createGet(PLUGIN_INFO_URL + plugin.getId())
.thenFunction(response -> {
try (InputStream is = response.bodyStream()) {
if (!response.isOk()) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_INFO_URL, response));
throw new IllegalArgumentException(CharSequenceUtil.format("{} 请求失败! = {}", PLUGIN_INFO_URL, response));
}
return IoUtil.readObj(is, PluginInfo.class);
PluginInfo pluginInfo = JSONUtil.toBean(IoUtil.readUtf8(is), PluginInfo.class);
log.info("已抓取 => ID = [{}], 名称 = [{}], Code = [{}]", pluginInfo.getId(), plugin.getName(), pluginInfo.getPurchaseInfo().getProductCode());
return pluginInfo;
} catch (IOException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} The request io read failed", PLUGIN_LIST_URL), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 请求IO读取失败!", PLUGIN_LIST_URL), e);
}
});
}

View File

@ -14,7 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.util.List;
@Slf4j
@Slf4j(topic = "产品上下文")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ProductsContextHolder {
@ -23,20 +23,20 @@ public class ProductsContextHolder {
private static List<ProductCache> productCacheList;
public static void init() {
log.info("Product context init loading...");
log.info("初始化中...");
File productJsonFile = FileTools.getFileOrCreat(PRODUCT_JSON_FILE_NAME);
String productJsonArray;
try {
productJsonArray = IoUtil.readUtf8(FileUtil.getInputStream(productJsonFile));
} catch (IORuntimeException e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File read failed !", PRODUCT_JSON_FILE_NAME), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件读取失败!", PRODUCT_JSON_FILE_NAME), e);
}
if (CharSequenceUtil.isBlank(productJsonArray) || !JSONUtil.isTypeJSON(productJsonArray)) {
log.error("Jetbrains Product data does not exist !");
log.error("产品数据不存在!");
} else {
productCacheList = JSONUtil.toList(productJsonArray, ProductCache.class);
log.info("Product context init success !");
log.info("初始化成功!");
}
}

View File

@ -35,7 +35,7 @@ public interface FileTools {
try {
FileUtil.writeFromStream(classPathResource.getInputStream(), classPathFile);
} catch (Exception e) {
throw new IllegalArgumentException(CharSequenceUtil.format("{} File read failed", classPathFile.getPath()), e);
throw new IllegalArgumentException(CharSequenceUtil.format("{} 文件读取失败!", classPathFile.getPath()), e);
}
FileUtil.copy(classPathFile, file, true);
}

View File

@ -1,10 +1,8 @@
${AnsiColor.RED} ██╗███████╗████████╗██████╗ ██████╗ █████╗ ██╗███╗ ██╗███████╗ ██╗ ██╗███████╗██╗ ██████╗
${AnsiColor.CYAN} ██║██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝ ██║ ██║██╔════╝██║ ██╔══██╗
${AnsiColor.BRIGHT_YELLOW} ██║█████╗ ██║ ██████╔╝██████╔╝███████║██║██╔██╗ ██║███████╗█████╗███████║█████╗ ██║ ██████╔╝
${AnsiColor.GREEN}██ ██║██╔══╝ ██║ ██╔══██╗██╔══██╗██╔══██║██║██║╚██╗██║╚════██║╚════╝██╔══██║██╔══╝ ██║ ██╔═══╝
${AnsiColor.BLUE}╚█████╔╝███████╗ ██║ ██████╔╝██║ ██║██║ ██║██║██║ ╚████║███████║ ██║ ██║███████╗███████╗██║
${AnsiColor.MAGENTA} ╚════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝
██╗███████╗████████╗██████╗ ██████╗ █████╗ ██╗███╗ ██╗███████╗ ██╗ ██╗███████╗██╗ ██████╗
██║██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝ ██║ ██║██╔════╝██║ ██╔══██╗
██║█████╗ ██║ ██████╔╝██████╔╝███████║██║██╔██╗ ██║███████╗█████╗███████║█████╗ ██║ ██████╔╝
██ ██║██╔══╝ ██║ ██╔══██╗██╔══██╗██╔══██║██║██║╚██╗██║╚════██║╚════╝██╔══██║██╔══╝ ██║ ██╔═══╝
╚█████╔╝███████╗ ██║ ██████╔╝██║ ██║██║ ██║██║██║ ╚████║███████║ ██║ ██║███████╗███████╗██║
╚════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝
${AnsiColor.BRIGHT_YELLOW} Spring Boot Version: ${spring-boot.version}
${AnsiColor.BRIGHT_YELLOW} Spring Boot Version: ${spring-boot.version}${AnsiColor.DEFAULT}

File diff suppressed because it is too large Load Diff