diff --git a/UPDATELOG.md b/UPDATELOG.md index 25b87c94f..444f22cd5 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -2,63 +2,50 @@ ### 🏆 重大改进 -- **核心架构升级**:与内核 `Mihomo` 采用 `IPC` 通信,不再依赖 `Restful API` 通信,提升性能和稳定性 -- **流量监控系统重构**:前端实现全新的增强流量监控系统,支持数据压缩、采样和智能缓存 -- **数据验证机制**:引入类型安全的数据验证器,确保 `API` 响应数据的一致性和可靠性 -- **配置缓存架构**:实现智能配置缓存系统,支持后端数据缓存和强制刷新机制 +- **核心通信架构升级**:采用全新通信机制,提升应用性能和稳定性 +- **流量监控系统重构**:全新的流量监控界面,支持更丰富的数据展示 +- **数据缓存优化**:改进配置和节点数据缓存,提升响应速度 ### ✨ 新增功能 - **Mihomo(Meta) 内核升级至 v1.19.12** -- 增加 `Verge Version` 复制按钮 -- 新增增强型流量监控 `Hook` 支持高级数据管理与采样 -- 支持原始/压缩流量数据处理与时间范围查询 -- 引用计数管理器智能收集数据 -- 新增流量监控诊断工具与错误边界组件 -- 多版本画布流量图表,丰富可视化选项 -- 新增强制刷新 `Clash` 配置/节点缓存功能,提升更新响应速度 -- 增加代理请求缓存机制,减少重复 `API` 调用 -- 添加首页卡片移动 (暂测) -- 首页流量统计卡片允许查看刻度线流量 +- 新增版本信息复制按钮 +- 增强型流量监控,支持更详细的数据分析 +- 新增流量图表多种显示模式 +- 新增强制刷新配置和节点缓存功能 +- 添加首页卡片移动功能(测试阶段) +- 首页流量统计支持查看刻度线详情 ### 🚀 性能优化 -- `IPC` 通信机制显著提升数据传输效率 -- 智能数据采样和压缩减少内存占用 -- 引用计数机制避免不必要的数据收集,提升整体性能 -- 优化流量图表渲染性能,支持大数据量展示 -- 改进前端数据获取和缓存策略 -- 实现配置/节点缓存 `TTL` 机制,减少不必要的配置/节点请求 -- 改进 `Clash` 配置/节点刷新间隔,从5秒优化到60秒,减少系统资源消耗 -- 同步设置页面所有按钮 +- 全面提升数据传输和处理效率 +- 优化内存使用,减少系统资源消耗 +- 改进流量图表渲染性能 +- 优化配置和节点刷新策略,从5秒延长到60秒 +- 改进数据缓存机制,减少重复请求 ### 🐞 修复问题 - 修复系统主题窗口颜色不一致问题 -- 修复 `URL` 编码处理,正确处理特殊字符 -- 增强代理更新的错误处理机制 -- 修复 `JSON` 解析错误处理 -- 优化调试日志输出,减少噪音 -- 修复配置修改后前端缓存不同步问题 -- 改进核心启动/停止/重启后的状态刷新机制 -- 修复 `Windows` 安装器删除用户自启问题 -- 修复 `Windows` 安装器参数使用错误问题 -- 修复 `macOS` 下点击 `Dock` 图标无法恢复窗口显示的问题 -- 修复 `IPC` 迁移后节点测速功能异常 -- 修复 `IPC` 迁移后连接上下行速率计算功能异常 -- 修复 `IPC` 迁移后内核日志功能异常 -- 修复 `External-Controller-Cors` 无法保存所需前置条件 -- 修复首页端口不一致问题 -- 修复首页流量统计卡片重构后无法显示流量刻度线 -- 修复日志页面启动/停止和清除按钮功能混淆,现在启动/停止按钮控制后端日志监控,清除按钮仅清理前端显示的日志 -- 修复日志等级设置的持久化配置,首次加载时正确应用已保存的日志等级到后端 +- 修复特殊字符 URL 处理问题 +- 修复配置修改后缓存不同步问题 +- 修复 Windows 安装器自启设置问题 +- 修复 macOS 下 Dock 图标恢复窗口问题 +- 修复架构升级后节点测速功能异常 +- 修复架构升级后流量统计功能异常 +- 修复架构升级后日志功能异常 +- 修复外部控制器跨域配置保存问题 +- 修复首页端口显示不一致问题 +- 修复首页流量统计刻度线显示问题 +- 修复日志页面按钮功能混淆问题 +- 修复日志等级设置保存问题 +- 修复偶发性启动卡死问题 ### 🔧 技术改进 -- 移除过时的 `Http` 控制 `Mihomo` 统一使用 `IPC` 控制 -- 添加外部控制器配置和 `UI` 支持 -- 改进 `IPC` 路径处理,支持 `Unix` 系统特定功能 -- 优化 `IPC` 目录安全检查和路径解析 +- 统一使用新的内核通信方式 +- 新增外部控制器配置界面 +- 改进跨平台兼容性支持 ## v2.3.2 diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index a8a7c6998..961bec884 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -216,7 +216,7 @@ pub async fn import_profile(url: String, option: Option) -> CmdResult } Ok(Err(e)) => { logging!(error, Type::Cmd, true, "[导入订阅] 导入失败: {}", e); - Err(format!("导入订阅失败: {}", e).into()) + Err(format!("导入订阅失败: {e}")) } Err(_) => { logging!(error, Type::Cmd, true, "[导入订阅] 导入超时(60秒): {}", url); diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index cdbd78770..c05645db4 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -127,7 +127,13 @@ pub async fn find_unused_port() -> Result { /// 异步方式处理启动后的额外任务 pub async fn resolve_setup_async(app_handle: &AppHandle) { let start_time = std::time::Instant::now(); - logging!(info, Type::Setup, true, "开始执行异步设置任务..."); + logging!( + info, + Type::Setup, + true, + "开始执行异步设置任务... 线程ID: {:?}", + std::thread::current().id() + ); if VERSION.get().is_none() { let version = app_handle.package_info().version.to_string(); @@ -151,16 +157,26 @@ pub async fn resolve_setup_async(app_handle: &AppHandle) { ); } - logging!(trace, Type::Config, true, "初始化配置..."); + logging!( + info, + Type::Config, + true, + "开始初始化配置... 线程ID: {:?}", + std::thread::current().id() + ); logging_error!(Type::Config, true, Config::init_config().await); + logging!(info, Type::Config, true, "配置初始化完成"); // 启动时清理冗余的 Profile 文件 - logging!(info, Type::Setup, true, "清理冗余的Profile文件..."); - let profiles = Config::profiles(); - if let Err(e) = profiles.latest_ref().auto_cleanup() { - logging!(warn, Type::Setup, true, "启动时清理Profile文件失败: {}", e); - } else { - logging!(info, Type::Setup, true, "启动时Profile文件清理完成"); + logging!(info, Type::Setup, true, "开始清理冗余的Profile文件..."); + + match Config::profiles().latest_ref().auto_cleanup() { + Ok(_) => { + logging!(info, Type::Setup, true, "启动时Profile文件清理完成"); + } + Err(e) => { + logging!(warn, Type::Setup, true, "启动时清理Profile文件失败: {}", e); + } } logging!(trace, Type::Core, true, "启动核心管理器..."); @@ -170,30 +186,6 @@ pub async fn resolve_setup_async(app_handle: &AppHandle) { server::embed_server(); logging!(trace, Type::Core, true, "启动 IPC 监控服务..."); - // IPC 监控器将在首次调用时自动初始化 - - // // 启动测试线程,持续打印流量数据 - // logging!(info, Type::Core, true, "启动流量数据测试线程..."); - // AsyncHandler::spawn(|| async { - // let mut interval = tokio::time::interval(std::time::Duration::from_secs(2)); - // loop { - // interval.tick().await; - - // let traffic_data = get_current_traffic().await; - // let memory_data = get_current_memory().await; - - // println!("=== Traffic Data Test (IPC) ==="); - // println!( - // "Traffic - Up: {} bytes/s, Down: {} bytes/s, Last Updated: {:?}", - // traffic_data.up_rate, traffic_data.down_rate, traffic_data.last_updated - // ); - // println!( - // "Memory - InUse: {} bytes, OSLimit: {:?}, Last Updated: {:?}", - // memory_data.inuse, memory_data.oslimit, memory_data.last_updated - // ); - // println!("=============================="); - // } - // }); logging_error!(Type::Tray, true, tray::Tray::global().init()); @@ -483,42 +475,82 @@ pub fn create_window(is_show: bool) -> bool { timeout_seconds ); - // 异步监控UI状态,不影响窗口显示 + // 异步监控UI状态,使用try_read避免死锁 tokio::spawn(async move { - let wait_result = - tokio::time::timeout(Duration::from_secs(timeout_seconds), async { - let mut check_count = 0; - while !*get_ui_ready().read() { - tokio::time::sleep(Duration::from_millis(100)).await; - check_count += 1; + logging!( + debug, + Type::Window, + true, + "启动UI状态监控线程,超时{}秒", + timeout_seconds + ); - // 每2秒记录一次等待状态 - if check_count % 20 == 0 { - logging!( - debug, - Type::Window, - true, - "UI加载状态检查... ({}秒)", - check_count / 10 - ); - } + let ui_ready_checker = || async { + let (mut check_count, mut consecutive_failures) = (0, 0); + + loop { + let is_ready = get_ui_ready() + .try_read() + .map(|guard| *guard) + .unwrap_or_else(|| { + consecutive_failures += 1; + if consecutive_failures > 50 { + logging!( + warn, + Type::Window, + true, + "UI状态监控连续{}次无法获取读锁,可能存在死锁", + consecutive_failures + ); + consecutive_failures = 0; + } + false + }); + + if is_ready { + logging!( + debug, + Type::Window, + true, + "UI状态监控检测到就绪信号,退出监控" + ); + return; } - }) - .await; + + consecutive_failures = 0; + tokio::time::sleep(Duration::from_millis(100)).await; + check_count += 1; + + if check_count % 20 == 0 { + logging!( + debug, + Type::Window, + true, + "UI加载状态检查... ({}秒)", + check_count / 10 + ); + } + } + }; + + let wait_result = tokio::time::timeout( + Duration::from_secs(timeout_seconds), + ui_ready_checker(), + ) + .await; match wait_result { Ok(_) => { logging!(info, Type::Window, true, "UI已完全加载就绪"); - // 移除初始加载指示器 - if let Some(window) = handle::Handle::global().get_window() { - let _ = window.eval(r#" + handle::Handle::global() + .get_window() + .map(|window| window.eval(r#" const overlay = document.getElementById('initial-loading-overlay'); if (overlay) { overlay.style.opacity = '0'; setTimeout(() => overlay.remove(), 300); } - "#); - } + "#)); } Err(_) => { logging!( @@ -528,7 +560,26 @@ pub fn create_window(is_show: bool) -> bool { "UI加载监控超时({}秒),但窗口已正常显示", timeout_seconds ); - *get_ui_ready().write() = true; + + get_ui_ready() + .try_write() + .map(|mut guard| { + *guard = true; + logging!( + info, + Type::Window, + true, + "超时后成功设置UI就绪状态" + ); + }) + .unwrap_or_else(|| { + logging!( + error, + Type::Window, + true, + "超时后无法获取UI状态写锁,可能存在严重死锁" + ); + }); } } }); @@ -633,24 +684,37 @@ async fn resolve_random_port_config() -> Result<()> { let port_to_save = port; - tokio::task::spawn_blocking(move || { - let verge_config_accessor = Config::verge(); - let mut verge_data = verge_config_accessor.data_mut(); - verge_data.patch_config(IVerge { - verge_mixed_port: Some(port_to_save), - ..IVerge::default() - }); - verge_data.save_file() - }) - .await??; // First ? for spawn_blocking error, second for save_file Result + // 合并配置访问以避免锁竞争死锁 + tokio::task::spawn_blocking(move || -> Result<()> { + logging!( + debug, + Type::Config, + true, + "开始合并配置更新操作,避免锁竞争" + ); - tokio::task::spawn_blocking(move || { - let clash_config_accessor = Config::clash(); // Extend lifetime of the accessor - let mut clash_data = clash_config_accessor.data_mut(); // Access within blocking task, made mutable - let mut mapping = Mapping::new(); - mapping.insert("mixed-port".into(), port_to_save.into()); - clash_data.patch_config(mapping); - clash_data.save_config() + // 按顺序更新配置,避免交叉锁定 + { + let verge_accessor = Config::verge(); + let mut verge_data = verge_accessor.data_mut(); + verge_data.patch_config(IVerge { + verge_mixed_port: Some(port_to_save), + ..IVerge::default() + }); + verge_data.save_file()?; + } + + { + let clash_accessor = Config::clash(); + let mut clash_data = clash_accessor.data_mut(); + let mut mapping = Mapping::new(); + mapping.insert("mixed-port".into(), port_to_save.into()); + clash_data.patch_config(mapping); + clash_data.save_config()?; + } + + logging!(debug, Type::Config, true, "配置更新操作完成"); + Ok(()) }) .await??;