feat: add network manager to optimize network request handling

This commit is contained in:
wonfen 2025-04-30 21:30:28 +08:00
parent 3ce5d3bf2d
commit 32b6821b8a
10 changed files with 340 additions and 96 deletions

View File

@ -30,6 +30,7 @@
- 定时自动订阅更新也能自动回退使用代理 - 定时自动订阅更新也能自动回退使用代理
- 订阅请求超时机制,防止订阅更新的时候卡死 - 订阅请求超时机制,防止订阅更新的时候卡死
- 订阅卡片点击时间可切换下次自动更新时间,自动更新触发后页面有明确的成功与否提示 - 订阅卡片点击时间可切换下次自动更新时间,自动更新触发后页面有明确的成功与否提示
- 添加网络管理器以优化网络请求处理,防止资源竞争导致的启动时 UI 卡死
#### 优化了: #### 优化了:
- 系统代理 Bypass 设置 - 系统代理 Bypass 设置

1
src-tauri/Cargo.lock generated
View File

@ -1060,6 +1060,7 @@ dependencies = [
"getrandom 0.3.2", "getrandom 0.3.2",
"image", "image",
"imageproc", "imageproc",
"lazy_static",
"libc", "libc",
"log", "log",
"log4rs", "log4rs",

View File

@ -30,6 +30,7 @@ boa_engine = "0.20.0"
serde_json = "1.0" serde_json = "1.0"
serde_yaml = "0.9" serde_yaml = "0.9"
once_cell = "1.21.3" once_cell = "1.21.3"
lazy_static = "1.4.0"
port_scanner = "0.1.5" port_scanner = "0.1.5"
delay_timer = "0.11.6" delay_timer = "0.11.6"
parking_lot = "0.12" parking_lot = "0.12"

View File

@ -1,10 +1,10 @@
use crate::utils::{dirs, help, resolve::VERSION, tmpl}; use crate::utils::network::{NetworkManager, ProxyType};
use crate::utils::{dirs, help, tmpl};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use reqwest::StatusCode; use reqwest::StatusCode;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::fs; use std::fs;
use sysproxy::Sysproxy;
use super::Config; use super::Config;
@ -254,58 +254,25 @@ impl PrfItem {
let mut proxies = opt_ref.and_then(|o| o.proxies.clone()); let mut proxies = opt_ref.and_then(|o| o.proxies.clone());
let mut groups = opt_ref.and_then(|o| o.groups.clone()); let mut groups = opt_ref.and_then(|o| o.groups.clone());
// 设置超时时间连接超时10秒请求总超时使用配置时间默认60秒 // 选择代理类型
let mut builder = reqwest::ClientBuilder::new() let proxy_type = if self_proxy {
.use_rustls_tls() ProxyType::SelfProxy
.no_proxy() } else if with_proxy {
.connect_timeout(std::time::Duration::from_secs(10)) ProxyType::SystemProxy
.timeout(std::time::Duration::from_secs(timeout)); } else {
ProxyType::NoProxy
// 使用软件自己的代理
if self_proxy {
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let proxy_scheme = format!("http://127.0.0.1:{port}");
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
// 使用系统代理
else if with_proxy {
if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
}
let version = match VERSION.get() {
Some(v) => format!("clash-verge/v{}", v),
None => "clash-verge/unknown".to_string(),
}; };
builder = builder.danger_accept_invalid_certs(accept_invalid_certs); // 使用网络管理器发送请求
builder = builder.user_agent(user_agent.unwrap_or(version)); let resp = NetworkManager::global()
.get(
let resp = builder.build()?.get(url).send().await?; url,
proxy_type,
Some(timeout),
user_agent.clone(),
accept_invalid_certs,
)
.await?;
let status_code = resp.status(); let status_code = resp.status();
if !StatusCode::is_success(&status_code) { if !StatusCode::is_success(&status_code) {

View File

@ -12,7 +12,6 @@ use std::{
process::Command as StdCommand, process::Command as StdCommand,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
use tokio::time::Duration;
// Windows only // Windows only
@ -49,7 +48,8 @@ impl ServiceState {
latest.service_state = Some(self.clone()); latest.service_state = Some(self.clone());
*config.draft() = latest; *config.draft() = latest;
config.apply(); config.apply();
Config::verge().latest().save_file() let result = config.latest().save_file();
result
} }
// 更新安装信息 // 更新安装信息
@ -482,13 +482,13 @@ pub async fn reinstall_service() -> Result<()> {
/// check the windows service status /// check the windows service status
pub async fn check_service() -> Result<JsonResponse> { pub async fn check_service() -> Result<JsonResponse> {
use crate::utils::network::{NetworkManager, ProxyType};
let url = format!("{SERVICE_URL}/get_clash"); let url = format!("{SERVICE_URL}/get_clash");
let response = reqwest::ClientBuilder::new()
.no_proxy() // 使用无代理模式和3秒超时检查服务
.timeout(Duration::from_secs(3)) let response = NetworkManager::global()
.build()? .get(&url, ProxyType::NoProxy, Some(3), None, false)
.get(url)
.send()
.await .await
.context("failed to connect to the Clash Verge Service")? .context("failed to connect to the Clash Verge Service")?
.json::<JsonResponse>() .json::<JsonResponse>()
@ -500,13 +500,13 @@ pub async fn check_service() -> Result<JsonResponse> {
/// check the service version /// check the service version
pub async fn check_service_version() -> Result<String> { pub async fn check_service_version() -> Result<String> {
use crate::utils::network::{NetworkManager, ProxyType};
let url = format!("{SERVICE_URL}/version"); let url = format!("{SERVICE_URL}/version");
let response = reqwest::ClientBuilder::new()
.no_proxy() // 使用无代理模式和3秒超时检查服务版本
.timeout(Duration::from_secs(3)) let response = NetworkManager::global()
.build()? .get(&url, ProxyType::NoProxy, Some(3), None, false)
.get(url)
.send()
.await .await
.context("failed to connect to the Clash Verge Service")? .context("failed to connect to the Clash Verge Service")?
.json::<VersionJsonResponse>() .json::<VersionJsonResponse>()
@ -568,6 +568,8 @@ pub async fn check_service_needs_reinstall() -> bool {
/// 尝试使用现有服务启动核心,不进行重装 /// 尝试使用现有服务启动核心,不进行重装
pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result<()> { pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result<()> {
use crate::utils::network::{NetworkManager, ProxyType};
log::info!(target:"app", "attempting to start core with existing service"); log::info!(target:"app", "attempting to start core with existing service");
let clash_core = { Config::verge().latest().clash_core.clone() }; let clash_core = { Config::verge().latest().clash_core.clone() };
@ -596,9 +598,10 @@ pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result
log::info!(target:"app", "start service: {:?}", map.clone()); log::info!(target:"app", "start service: {:?}", map.clone());
let url = format!("{SERVICE_URL}/start_clash"); let url = format!("{SERVICE_URL}/start_clash");
let _ = reqwest::ClientBuilder::new()
.no_proxy() // 使用网络管理器发送POST请求
.build()? let client = NetworkManager::global().get_client(ProxyType::NoProxy);
let _ = client
.post(url) .post(url)
.json(&map) .json(&map)
.send() .send()
@ -710,10 +713,13 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
/// stop the clash by service /// stop the clash by service
pub(super) async fn stop_core_by_service() -> Result<()> { pub(super) async fn stop_core_by_service() -> Result<()> {
use crate::utils::network::{NetworkManager, ProxyType};
let url = format!("{SERVICE_URL}/stop_clash"); let url = format!("{SERVICE_URL}/stop_clash");
let _ = reqwest::ClientBuilder::new()
.no_proxy() // 使用网络管理器发送POST请求
.build()? let client = NetworkManager::global().get_client(ProxyType::NoProxy);
let _ = client
.post(url) .post(url)
.send() .send()
.await .await

View File

@ -91,36 +91,27 @@ pub fn change_clash_mode(mode: String) {
/// Test connection delay to a URL /// Test connection delay to a URL
pub async fn test_delay(url: String) -> anyhow::Result<u32> { pub async fn test_delay(url: String) -> anyhow::Result<u32> {
use tokio::time::{Duration, Instant}; use crate::utils::network::{NetworkManager, ProxyType};
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy(); use tokio::time::Instant;
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let tun_mode = Config::verge().latest().enable_tun_mode.unwrap_or(false); let tun_mode = Config::verge().latest().enable_tun_mode.unwrap_or(false);
let proxy_scheme = format!("http://127.0.0.1:{port}"); // 如果是TUN模式不使用代理否则使用自身代理
let proxy_type = if !tun_mode {
ProxyType::SelfProxy
} else {
ProxyType::NoProxy
};
if !tun_mode { let user_agent = Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0".to_string());
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
let request = builder
.timeout(Duration::from_millis(10000))
.build()?
.get(url).header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");
let start = Instant::now(); let start = Instant::now();
let response = request.send().await; // 使用网络管理器发送请求设置10秒超时
let response = NetworkManager::global()
.get(&url, proxy_type, Some(10), user_agent, false)
.await;
match response { match response {
Ok(response) => { Ok(response) => {
log::trace!(target: "app", "test_delay response: {:#?}", response); log::trace!(target: "app", "test_delay response: {:#?}", response);
@ -132,7 +123,7 @@ pub async fn test_delay(url: String) -> anyhow::Result<u32> {
} }
Err(err) => { Err(err) => {
log::trace!(target: "app", "test_delay error: {:#?}", err); log::trace!(target: "app", "test_delay error: {:#?}", err);
Err(err.into()) Err(err)
} }
} }
} }

View File

@ -86,6 +86,9 @@ impl AppHandleManager {
#[allow(clippy::panic)] #[allow(clippy::panic)]
pub fn run() { pub fn run() {
// 初始化网络管理器
utils::network::NetworkManager::global().init();
// 单例检测 - 使用超时机制防止阻塞 // 单例检测 - 使用超时机制防止阻塞
let app_exists: bool = AsyncHandler::block_on(move || async move { let app_exists: bool = AsyncHandler::block_on(move || async move {
match timeout(Duration::from_secs(3), server::check_singleton()).await { match timeout(Duration::from_secs(3), server::check_singleton()).await {

View File

@ -15,6 +15,7 @@ pub enum Type {
Frontend, Frontend,
Backup, Backup,
Lightweight, Lightweight,
Network,
} }
impl fmt::Display for Type { impl fmt::Display for Type {
@ -33,6 +34,7 @@ impl fmt::Display for Type {
Type::Frontend => write!(f, "[Frontend]"), Type::Frontend => write!(f, "[Frontend]"),
Type::Backup => write!(f, "[Backup]"), Type::Backup => write!(f, "[Backup]"),
Type::Lightweight => write!(f, "[Lightweight]"), Type::Lightweight => write!(f, "[Lightweight]"),
Type::Network => write!(f, "[Network]"),
} }
} }
} }

View File

@ -5,6 +5,7 @@ pub mod help;
pub mod i18n; pub mod i18n;
pub mod init; pub mod init;
pub mod logging; pub mod logging;
pub mod network;
pub mod resolve; pub mod resolve;
pub mod server; pub mod server;
pub mod tmpl; pub mod tmpl;

View File

@ -0,0 +1,271 @@
use anyhow::{Context, Result};
use lazy_static::lazy_static;
use reqwest::{Client, ClientBuilder, Proxy, RequestBuilder, Response};
use std::sync::{Arc, Mutex, Once};
use std::time::Duration;
use tokio::runtime::{Builder, Runtime};
use crate::{config::Config, logging, utils::logging::Type};
/// 网络管理器
pub struct NetworkManager {
runtime: Arc<Runtime>,
self_proxy_client: Arc<Mutex<Option<Client>>>,
system_proxy_client: Arc<Mutex<Option<Client>>>,
no_proxy_client: Arc<Mutex<Option<Client>>>,
init: Once,
}
lazy_static! {
static ref NETWORK_MANAGER: NetworkManager = NetworkManager::new();
}
impl NetworkManager {
fn new() -> Self {
// 创建专用的异步运行时线程数限制为4个
let runtime = Builder::new_multi_thread()
.worker_threads(4)
.thread_name("clash-verge-network")
.enable_io()
.enable_time()
.build()
.expect("Failed to create network runtime");
NetworkManager {
runtime: Arc::new(runtime),
self_proxy_client: Arc::new(Mutex::new(None)),
system_proxy_client: Arc::new(Mutex::new(None)),
no_proxy_client: Arc::new(Mutex::new(None)),
init: Once::new(),
}
}
pub fn global() -> &'static Self {
&NETWORK_MANAGER
}
/// 初始化网络客户端
pub fn init(&self) {
self.init.call_once(|| {
self.runtime.spawn(async {
logging!(info, Type::Network, true, "初始化网络管理器");
// 创建无代理客户端
let no_proxy_client = ClientBuilder::new()
.use_rustls_tls()
.no_proxy()
.pool_max_idle_per_host(5)
.pool_idle_timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(30))
.build()
.expect("Failed to build no_proxy client");
let mut no_proxy_guard = NETWORK_MANAGER.no_proxy_client.lock().unwrap();
*no_proxy_guard = Some(no_proxy_client);
logging!(info, Type::Network, true, "网络管理器初始化完成");
});
});
}
/// 获取或创建自代理客户端
fn get_or_create_self_proxy_client(&self) -> Client {
let mut client_guard = self.self_proxy_client.lock().unwrap();
if client_guard.is_none() {
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let proxy_scheme = format!("http://127.0.0.1:{port}");
let mut builder = ClientBuilder::new()
.use_rustls_tls()
.pool_max_idle_per_host(5)
.pool_idle_timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(60));
// 添加所有代理类型
if let Ok(proxy) = Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
let client = builder.build().expect("Failed to build self_proxy client");
*client_guard = Some(client);
}
client_guard.as_ref().unwrap().clone()
}
/// 获取或创建系统代理客户端
fn get_or_create_system_proxy_client(&self) -> Client {
let mut client_guard = self.system_proxy_client.lock().unwrap();
if client_guard.is_none() {
use sysproxy::Sysproxy;
let mut builder = ClientBuilder::new()
.use_rustls_tls()
.pool_max_idle_per_host(5)
.pool_idle_timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(60));
if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(proxy) = Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
let client = builder
.build()
.expect("Failed to build system_proxy client");
*client_guard = Some(client);
}
client_guard.as_ref().unwrap().clone()
}
/// 根据代理设置选择合适的客户端
pub fn get_client(&self, proxy_type: ProxyType) -> Client {
match proxy_type {
ProxyType::NoProxy => {
let client_guard = self.no_proxy_client.lock().unwrap();
client_guard.as_ref().unwrap().clone()
}
ProxyType::SelfProxy => self.get_or_create_self_proxy_client(),
ProxyType::SystemProxy => self.get_or_create_system_proxy_client(),
}
}
/// 创建带有自定义选项的HTTP请求
pub fn create_request(
&self,
url: &str,
proxy_type: ProxyType,
timeout_secs: Option<u64>,
user_agent: Option<String>,
accept_invalid_certs: bool,
) -> RequestBuilder {
let mut builder = ClientBuilder::new()
.use_rustls_tls()
.connect_timeout(Duration::from_secs(10));
// 超时
if let Some(timeout) = timeout_secs {
builder = builder.timeout(Duration::from_secs(timeout));
} else {
builder = builder.timeout(Duration::from_secs(60));
}
// 设置代理
match proxy_type {
ProxyType::NoProxy => {
builder = builder.no_proxy();
}
ProxyType::SelfProxy => {
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let proxy_scheme = format!("http://127.0.0.1:{port}");
if let Ok(proxy) = Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
ProxyType::SystemProxy => {
use sysproxy::Sysproxy;
if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(proxy) = Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
}
}
// 证书验证选项
builder = builder.danger_accept_invalid_certs(accept_invalid_certs);
// 用户代理
if let Some(ua) = user_agent {
builder = builder.user_agent(ua);
} else {
use crate::utils::resolve::VERSION;
let version = match VERSION.get() {
Some(v) => format!("clash-verge/v{}", v),
None => "clash-verge/unknown".to_string(),
};
builder = builder.user_agent(version);
}
// 构建请求
let client = builder.build().expect("Failed to build custom HTTP client");
client.get(url)
}
/// 执行GET请求
pub async fn get(
&self,
url: &str,
proxy_type: ProxyType,
timeout_secs: Option<u64>,
user_agent: Option<String>,
accept_invalid_certs: bool,
) -> Result<Response> {
self.create_request(
url,
proxy_type,
timeout_secs,
user_agent,
accept_invalid_certs,
)
.send()
.await
.context("Failed to send HTTP request")
}
}
/// 代理类型
#[derive(Debug, Clone, Copy)]
pub enum ProxyType {
NoProxy,
SelfProxy,
SystemProxy,
}