feat: add retry mechanism to optimize network requests, improve async message handling and timeout for scheduled tasks

This commit is contained in:
wonfen 2025-05-03 09:38:25 +08:00
parent e7bf997f8c
commit 73b9a71c84
4 changed files with 102 additions and 39 deletions

View File

@ -265,7 +265,7 @@ impl PrfItem {
// 使用网络管理器发送请求 // 使用网络管理器发送请求
let resp = NetworkManager::global() let resp = NetworkManager::global()
.get( .get_with_retry(
url, url,
proxy_type, proxy_type,
Some(timeout), Some(timeout),

View File

@ -108,15 +108,30 @@ impl Handle {
return; return;
} }
// 已经完成启动,直接发送消息 // 使用AsyncHandler发送消息防止阻塞
if let Some(window) = handle.get_window() { let status_clone = status_str.clone();
logging_error!( let msg_clone = msg_str.clone();
Type::Frontend,
true, crate::process::AsyncHandler::spawn(move || async move {
window.emit("verge://notice-message", (status_str, msg_str)) let handle_clone = Self::global();
); if let Some(window) = handle_clone.get_window() {
match tokio::time::timeout(tokio::time::Duration::from_millis(500), async {
window.emit("verge://notice-message", (status_clone, msg_clone))
})
.await
{
Ok(result) => {
if let Err(e) = result {
logging!(warn, Type::Frontend, true, "发送通知消息失败: {}", e);
} }
} }
Err(_) => {
logging!(warn, Type::Frontend, true, "发送通知消息超时");
}
}
}
});
}
/// 标记启动已完成,并发送所有启动阶段累积的错误消息 /// 标记启动已完成,并发送所有启动阶段累积的错误消息
pub fn mark_startup_completed(&self) { pub fn mark_startup_completed(&self) {
@ -158,7 +173,6 @@ impl Handle {
for error in errors_clone { for error in errors_clone {
let _ = let _ =
window_clone.emit("verge://notice-message", (error.status, error.message)); window_clone.emit("verge://notice-message", (error.status, error.message));
// 每条消息之间间隔500ms避免消息堆积
tokio::time::sleep(Duration::from_millis(500)).await; tokio::time::sleep(Duration::from_millis(500)).await;
} }
}); });

View File

@ -385,7 +385,6 @@ impl Timer {
} }
}; };
// 修复类型比较,使用字符串值比较而不是引用比较
let profile = match items let profile = match items
.iter() .iter()
.find(|item| item.uid.as_ref().map(|u| u.as_str()) == Some(uid)) .find(|item| item.uid.as_ref().map(|u| u.as_str()) == Some(uid))
@ -424,7 +423,6 @@ impl Timer {
/// Emit update events for frontend notification /// Emit update events for frontend notification
fn emit_update_event(_uid: &str, _is_start: bool) { fn emit_update_event(_uid: &str, _is_start: bool) {
// 当feature="verge-dev"或"default"时才启用此功能
#[cfg(any(feature = "verge-dev", feature = "default"))] #[cfg(any(feature = "verge-dev", feature = "default"))]
{ {
use serde_json::json; use serde_json::json;
@ -443,14 +441,14 @@ impl Timer {
} }
/// Async task with better error handling and logging /// Async task with better error handling and logging
async fn async_task(uid: String) { async fn async_task(uid: String) {
let task_start = std::time::Instant::now(); let task_start = std::time::Instant::now();
logging!(info, Type::Timer, "Running timer task for profile: {}", uid); logging!(info, Type::Timer, "Running timer task for profile: {}", uid);
// Emit start event match tokio::time::timeout(std::time::Duration::from_secs(40), async {
Self::emit_update_event(&uid, true); Self::emit_update_event(&uid, true);
// 检查是否是当前激活的配置文件
let is_current = Config::profiles().latest().current.as_ref() == Some(&uid); let is_current = Config::profiles().latest().current.as_ref() == Some(&uid);
logging!( logging!(
info, info,
@ -460,10 +458,11 @@ impl Timer {
is_current is_current
); );
// Update profile - 由update_profile函数自动处理是否需要刷新UI feat::update_profile(uid.clone(), None, Some(is_current)).await
let profile_result = feat::update_profile(uid.clone(), None, Some(is_current)).await; })
.await
match profile_result { {
Ok(result) => match result {
Ok(_) => { Ok(_) => {
let duration = task_start.elapsed().as_millis(); let duration = task_start.elapsed().as_millis();
logging!( logging!(
@ -477,6 +476,10 @@ impl Timer {
Err(e) => { Err(e) => {
logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e); logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e);
} }
},
Err(_) => {
logging_error!(Type::Timer, false, "Timer task timed out for uid: {}", uid);
}
} }
// Emit completed event // Emit completed event

View File

@ -260,6 +260,52 @@ impl NetworkManager {
.await .await
.context("Failed to send HTTP request") .context("Failed to send HTTP request")
} }
pub async fn get_with_retry(
&self,
url: &str,
proxy_type: ProxyType,
timeout_secs: Option<u64>,
user_agent: Option<String>,
accept_invalid_certs: bool,
) -> Result<Response> {
let max_retries = 2;
let mut last_error = None;
for attempt in 0..=max_retries {
if attempt > 0 {
logging!(info, Type::Network, "重试第{}次请求: {}", attempt, url);
tokio::time::sleep(Duration::from_millis(500)).await;
}
match self
.get(
url,
proxy_type,
timeout_secs,
user_agent.clone(),
accept_invalid_certs,
)
.await
{
Ok(resp) => return Ok(resp),
Err(e) => {
logging!(
warn,
Type::Network,
"请求失败 (尝试 {}/{}): {} - {}",
attempt + 1,
max_retries + 1,
url,
e
);
last_error = Some(e);
continue;
}
}
}
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("请求失败,但没有具体错误信息")))
}
} }
/// 代理类型 /// 代理类型