feat: Implement configuration caching mechanism and force-refresh feature**

 **New Features**:

* Added API and frontend support for forcibly refreshing Clash configuration cache
* Implemented a configuration cache TTL mechanism (60 seconds) to reduce redundant requests
* Introduced `ProxyRequestCache` system to manage backend data caching
* Automatically refresh frontend state after core operations to enhance user experience

🚀 **Performance Optimizations**:

* Increased Clash configuration refresh interval from 5 seconds to 60 seconds
* Force refresh cache after configuration updates to resolve data inconsistency
* Automatically trigger state refresh after core switch, start, stop, and restart actions

🔧 **Technical Improvements**:

* Removed unused dependencies: `ab_glyph`, `owned_ttf_parser`, `ttf-parser`
* Simplified WebSocket dependency management, unified `tungstenite` version
* Refactored configuration save validation process, improved merge file handling
* Improved error handling and overall user experience
This commit is contained in:
Tunglies
2025-07-24 01:56:18 +08:00
parent 27535c7bb7
commit 3048a2ae08
10 changed files with 83 additions and 99 deletions

View File

@@ -5,6 +5,7 @@
- **核心架构升级**:与内核 Mihomo 采用 IPC 通信,不再依赖 Restful API 通信,提升性能和稳定性
- **流量监控系统重构**:前端实现全新的增强流量监控系统,支持数据压缩、采样和智能缓存
- **数据验证机制**:引入类型安全的数据验证器,确保 API 响应数据的一致性和可靠性
- **配置缓存架构**:实现智能配置缓存系统,支持后端数据缓存和强制刷新机制
### ✨ 新增功能
@@ -14,6 +15,8 @@
- 引用计数管理器智能收集数据
- 新增流量监控诊断工具与错误边界组件
- 多版本画布流量图表,丰富可视化选项
- 新增强制刷新 Clash 配置/节点缓存功能,提升更新响应速度
- 增加代理请求缓存机制,减少重复 API 调用
### 🚀 性能优化
@@ -22,6 +25,8 @@
- 引用计数机制避免不必要的数据收集,提升整体性能
- 优化流量图表渲染性能,支持大数据量展示
- 改进前端数据获取和缓存策略
- 实现配置/节点缓存 TTL 机制,减少不必要的配置/节点请求
- 改进 Clash 配置/节点刷新间隔从5秒优化到60秒减少系统资源消耗
### 🐞 修复问题
@@ -30,6 +35,8 @@
- 增强代理更新的错误处理机制
- 修复 JSON 解析错误处理
- 优化调试日志输出,减少噪音
- 修复配置修改后前端缓存不同步问题
- 改进核心启动/停止/重启后的状态刷新机制
### 🔧 技术改进

85
src-tauri/Cargo.lock generated
View File

@@ -2,22 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "ab_glyph"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"
[[package]]
name = "addr2line"
version = "0.24.2"
@@ -1125,7 +1109,6 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
name = "clash-verge"
version = "2.4.0"
dependencies = [
"ab_glyph",
"aes-gcm",
"anyhow",
"async-trait",
@@ -1185,8 +1168,6 @@ dependencies = [
"tauri-plugin-window-state",
"tempfile",
"tokio",
"tokio-tungstenite 0.24.0",
"tungstenite 0.27.0",
"users",
"warp",
"winapi",
@@ -4933,15 +4914,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owned_ttf_parser"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
dependencies = [
"ttf-parser",
]
[[package]]
name = "pango"
version = "0.18.3"
@@ -7898,19 +7870,7 @@ dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.21.0",
]
[[package]]
name = "tokio-tungstenite"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.24.0",
"tungstenite",
]
[[package]]
@@ -8274,12 +8234,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "tungstenite"
version = "0.21.0"
@@ -8299,41 +8253,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "tungstenite"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.3.1",
"httparse",
"log",
"rand 0.8.5",
"sha1",
"thiserror 1.0.69",
"utf-8",
]
[[package]]
name = "tungstenite"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
dependencies = [
"bytes",
"data-encoding",
"http 1.3.1",
"httparse",
"log",
"rand 0.9.2",
"sha1",
"thiserror 2.0.12",
"utf-8",
]
[[package]]
name = "typeid"
version = "1.0.3"
@@ -8644,7 +8563,7 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-tungstenite 0.21.0",
"tokio-tungstenite",
"tokio-util",
"tower-service",
"tracing",

View File

@@ -1,6 +1,12 @@
use super::CmdResult;
use crate::{config::*, core::*, feat, ipc::IpcManager, process::AsyncHandler, wrap_err};
use crate::{
config::*, core::*, feat, ipc::IpcManager, process::AsyncHandler,
state::proxy::ProxyRequestCache, wrap_err,
};
use serde_yaml::Mapping;
use std::time::Duration;
const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
/// 复制Clash环境变量
#[tauri::command]
@@ -66,19 +72,31 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
/// 启动核心
#[tauri::command]
pub async fn start_core() -> CmdResult {
wrap_err!(CoreManager::global().start_core().await)
let result = wrap_err!(CoreManager::global().start_core().await);
if result.is_ok() {
handle::Handle::refresh_clash();
}
result
}
/// 关闭核心
#[tauri::command]
pub async fn stop_core() -> CmdResult {
wrap_err!(CoreManager::global().stop_core().await)
let result = wrap_err!(CoreManager::global().stop_core().await);
if result.is_ok() {
handle::Handle::refresh_clash();
}
result
}
/// 重启核心
#[tauri::command]
pub async fn restart_core() -> CmdResult {
wrap_err!(CoreManager::global().restart_core().await)
let result = wrap_err!(CoreManager::global().restart_core().await);
if result.is_ok() {
handle::Handle::refresh_clash();
}
result
}
/// 获取代理延迟
@@ -277,7 +295,24 @@ pub async fn get_clash_version() -> CmdResult<serde_json::Value> {
/// 获取Clash配置
#[tauri::command]
pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_config().await)
let manager = IpcManager::global();
let cache = ProxyRequestCache::global();
let key = ProxyRequestCache::make_key("clash_config", "default");
let value = cache
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
manager.get_config().await.expect("fetch failed")
})
.await;
Ok((*value).clone())
}
/// 强制刷新Clash配置缓存
#[tauri::command]
pub async fn force_refresh_clash_config() -> CmdResult<serde_json::Value> {
let cache = ProxyRequestCache::global();
let key = ProxyRequestCache::make_key("clash_config", "default");
cache.map.remove(&key);
get_clash_config().await
}
/// 更新地理数据

View File

@@ -61,7 +61,12 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
"[cmd配置save] merge文件语法验证通过"
);
// 成功后尝试更新整体配置
if let Err(e) = CoreManager::global().update_config().await {
match CoreManager::global().update_config().await {
Ok(_) => {
// 配置更新成功,刷新前端
handle::Handle::refresh_clash();
}
Err(e) => {
logging!(
warn,
Type::Config,
@@ -70,6 +75,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
e
);
}
}
return Ok(());
}
Ok((false, error_msg)) => {

View File

@@ -274,6 +274,7 @@ pub fn run() {
cmd::validate_dns_config,
cmd::get_clash_version,
cmd::get_clash_config,
cmd::force_refresh_clash_config,
cmd::update_geo_data,
cmd::upgrade_clash_core,
cmd::get_clash_rules,

View File

@@ -18,7 +18,11 @@ import {
ListItemText,
} from "@mui/material";
import { changeClashCore, restartCore } from "@/services/cmds";
import { closeAllConnections, upgradeCore } from "@/services/cmds";
import {
closeAllConnections,
upgradeCore,
forceRefreshClashConfig,
} from "@/services/cmds";
import { showNotice } from "@/services/noticeService";
const VALID_CORE = [
@@ -58,7 +62,9 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
}
mutateVerge();
setTimeout(() => {
setTimeout(async () => {
// 核心切换后强制刷新配置缓存
await forceRefreshClashConfig();
mutate("getClashConfig");
mutate("getVersion");
setChangingCore(null);

View File

@@ -5,6 +5,7 @@ import {
getClashInfo,
patchClashConfig,
getRuntimeConfig,
forceRefreshClashConfig,
} from "@/services/cmds";
export const useClash = () => {
@@ -121,6 +122,8 @@ export const useClashInfo = () => {
await patchClashConfig(patch);
mutateInfo();
// 配置修改后强制刷新缓存
await forceRefreshClashConfig();
mutate("getClashConfig");
// IPC调用不需要刷新axios实例
};

View File

@@ -9,6 +9,7 @@ import { List, Paper, ThemeProvider, SvgIcon } from "@mui/material";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { routers } from "./_routers";
import { getAxios } from "@/services/api";
import { forceRefreshClashConfig } from "@/services/cmds";
import { useVerge } from "@/hooks/use-verge";
import LogoSvg from "@/assets/image/logo.svg?react";
import iconLight from "@/assets/image/icon_light.svg?react";
@@ -192,6 +193,8 @@ const Layout = () => {
const listeners = [
addListener("verge://refresh-clash-config", async () => {
await getAxios(true);
// 后端配置变更事件触发,强制刷新配置缓存
await forceRefreshClashConfig();
mutate("getProxies");
mutate("getVersion");
mutate("getClashConfig");

View File

@@ -185,7 +185,7 @@ export const AppDataProvider = ({
"getClashConfig",
getClashConfig,
{
refreshInterval: 5000,
refreshInterval: 60000, // 60秒刷新间隔减少频繁请求
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3,

View File

@@ -107,6 +107,10 @@ export async function getClashConfig() {
return invoke<IConfigData>("get_clash_config");
}
export async function forceRefreshClashConfig() {
return invoke<IConfigData>("force_refresh_clash_config");
}
export async function updateGeoData() {
return invoke<void>("update_geo_data");
}