remove updates

This commit is contained in:
2026-04-05 22:51:11 +07:00
parent aa66d93dbf
commit 6b5f7b23d8
9 changed files with 197 additions and 187 deletions
+22 -19
View File
@@ -6,18 +6,22 @@ pub static GLOBAL_NET_CONFIG: OnceLock<NetworkConfig> = OnceLock::new();
#[derive(Debug, Clone)]
pub struct NetworkConfig {
pub mtu: usize,
pub tcp_buffer_size: usize,
pub udp_buffer_size: usize,
// Очереди MPSC
pub muxer_capacity: usize,
pub tcp_stream_capacity: usize,
pub udp_stream_capacity: usize,
// --- Очереди MPSC (Разделенные) ---
// Для Клиента (мобильный интернет)
pub client_muxer_capacity: usize, // Подушка для 10 ног
pub client_tun_capacity: usize, // Стык TUN <-> Engine
pub client_stream_capacity: usize, // Быстрый Backpressure для сокета
// Для Сервера (Дата-центр)
pub server_muxer_capacity: usize, // Огромная очередь для входящего трафика
pub server_stream_capacity: usize, // Чтобы сервер не тормозил на отдачу
pub tcp_chunk_size: usize,
// 👈 Разделяем буферы на RX (Чтение/Download) и TX (Запись/Upload)
// Буферы сокетов smoltcp
pub tcp_rx_heavy: usize,
pub tcp_tx_heavy: usize,
pub tcp_rx_light: usize,
@@ -33,28 +37,27 @@ impl NetworkConfig {
pub fn new(system_mtu: usize) -> Self {
Self {
mtu: system_mtu,
tcp_buffer_size: 8 * 1024,
udp_buffer_size: 64 * 1024,
// 1. Убиваем лаги Муксера: меньше сообщений в очереди
muxer_capacity: 256, // Было 32
tcp_stream_capacity: 16,
udp_stream_capacity: 32,
tcp_chunk_size: 16 * 1024,
// 2. Делаем чанки меньше для более быстрого срабатывания Backpressure
tcp_chunk_size: 4 * 1024, // Было 16KB
client_muxer_capacity: 256,
client_tun_capacity: 16,
client_stream_capacity: 32,
// 3. АСИММЕТРИЧНЫЕ БУФЕРЫ TCP (Магия хорошего Upload)
tcp_rx_heavy: 256 * 1024, // 1 МБ! Качаем на все бабки (Быстрый Download)
tcp_tx_heavy: 32 * 1024, // 64 КБ! Микро-буфер отдачи (Убивает Bufferbloat на Upload)
// --- СЕРВЕР ---
server_muxer_capacity: 128,
server_stream_capacity: 32,
tcp_rx_light: 16 * 1024,
tcp_tx_light: 16 * 1024,
tcp_rx_heavy: 256 * 1024,
tcp_tx_heavy: 512 * 1024,
tcp_rx_light: 256 * 1024,
tcp_tx_light: 32 * 1024,
udp_buf_heavy: 256 * 1024,
udp_meta_heavy: 512,
udp_buf_light: 16 * 1024,
udp_meta_light: 32,
}
+40 -13
View File
@@ -111,13 +111,44 @@ impl ClientHandler {
leg_name, leg_id, remote_proxy_addr
);
let stream = TcpStream::connect(remote_proxy_addr)
// 1. Резолвим домен в IP-адрес
let mut addrs = tokio::net::lookup_host(remote_proxy_addr)
.await
.map_err(|e| format!("Failed to connect: {}", e))?;
.map_err(|e| format!("DNS resolution failed for {}: {}", remote_proxy_addr, e))?;
let addr = addrs
.next()
.ok_or_else(|| format!("No IPs found for {}", remote_proxy_addr))?;
// 2. Создаем сокет
let socket = if addr.is_ipv4() {
tokio::net::TcpSocket::new_v4().map_err(|e| e.to_string())?
} else {
tokio::net::TcpSocket::new_v6().map_err(|e| e.to_string())?
};
// 3. 🛡️ БАЛАНСИРУЕМ БУФЕРЫ ЯДРА
// 256 KB для отправки (Send) — это "золотая середина" для пинга ~300мс.
// Математически это позволит разогнать Upload до ~7-8 Мбит/с без 9-секундных лагов.
if let Err(e) = socket.set_send_buffer_size(NetworkConfig::global().tcp_tx_heavy as u32) {
warn!("Failed to set SO_SNDBUF on {}: {}", leg_name, e);
}
// 🚨 ВАЖНО: Мы НЕ ставим set_recv_buffer_size вручную.
// Это позволяет ОС использовать TCP Window Scaling и Auto-tuning,
// что вернет твою скорость Download к 80+ Мбит/с.
// 4. Подключаемся
let stream = socket
.connect(addr)
.await
.map_err(|e| format!("Failed to connect to {}: {}", addr, e))?;
let _ = stream.set_nodelay(true);
let (inbound, outbound) = stream.into_split();
let mut conn = Connection::new_raw(inbound, outbound);
// --- Дальше твой TLS Handshake без изменений ---
let ch = conn
.codec
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
@@ -146,6 +177,8 @@ impl ClientHandler {
}
info!("{} TLS Handshake complete.", leg_name);
// ... (остальной код инициализации TunnelEngine остается прежним)
let handshake_payload = Bytes::from(format!("{}:{}", session_id, leg_id));
let encrypted_handshake = conn
@@ -158,13 +191,8 @@ impl ClientHandler {
.await
.map_err(|e| format!("Failed to send Handshake: {}", e))?;
info!(
"{} Sent Encrypted Handshake (Leg: {}). Starting engine...",
leg_name, leg_id
);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().client_muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().client_muxer_capacity);
muxer.add_leg(leg_id, control_tx, data_tx);
@@ -262,7 +290,7 @@ impl ClientHandler {
);
let (v_tx, mut v_rx) =
mpsc::channel(NetworkConfig::global().tcp_stream_capacity);
mpsc::channel(NetworkConfig::global().client_stream_capacity);
muxer_inner.register_stream(global_stream_id, v_tx);
@@ -307,7 +335,6 @@ impl ClientHandler {
local_to_global.get(&local_socket_id).map(|r| *r);
if let Some(id) = global_stream_id {
// ✅ Добавлен .await
let _ = muxer_inner.send_data_safe(id, payload, is_udp).await;
} else {
warn!(
@@ -511,8 +538,8 @@ impl TunnelHandler for ServerHandler {
let muxer = self.session_manager.get_or_create(&session_id);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().server_muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().server_muxer_capacity);
muxer.add_leg(leg_id, control_tx, data_tx);
let opener = Arc::new(RemoteOpener {
+1
View File
@@ -206,6 +206,7 @@ impl TunnelEngine {
error!(stream_id, error = %e, "Failed to write encrypted data to network");
e.to_string()
})?;
tokio::task::yield_now().await;
}
debug!(stream_id, "Outbound packet sent successfully");
+2 -2
View File
@@ -101,9 +101,9 @@ impl StreamHandler {
if let Some(opener) = &self.opener {
let capacity = if is_udp {
NetworkConfig::global().udp_stream_capacity
NetworkConfig::global().server_stream_capacity
} else {
NetworkConfig::global().tcp_stream_capacity
NetworkConfig::global().server_stream_capacity
};
let (v_tx, v_rx) = mpsc::channel(capacity);
+72 -89
View File
@@ -1,12 +1,11 @@
use bytes::Bytes;
use dashmap::DashMap;
use netrunner_logger::{debug, info, warn};
use netrunner_logger::{debug, info, trace, warn};
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::mpsc::Sender;
// Подключаем твои константы (убедись, что путь импорта совпадает с твоей структурой)
use crate::net::constants::{HEALTH_CHECK_TIMEOUT, MAX_TUNNEL_LEGS, MUXER_POOL_SIZE};
use crate::net::{HEALTH_CHECK_TIMEOUT, MAX_TUNNEL_LEGS, MUXER_POOL_SIZE};
use crate::nrxp::FrameType;
#[derive(Default, Debug)]
@@ -14,7 +13,6 @@ pub struct LegStats {
pub tx_bytes: AtomicU64,
pub rx_bytes: AtomicU64,
pub rtt_ms: AtomicU32,
pub strikes: AtomicU32, // 👈 Счетчик пропущенных пингов (страйков)
}
#[derive(Default, Debug)]
@@ -57,6 +55,8 @@ pub struct MuxMessage {
pub struct Muxer {
legs: Arc<DashMap<u32, MuxLeg>>,
streams: Arc<DashMap<u32, (Sender<Bytes>, Arc<StreamStats>)>>,
// 🚨 Sticky Sessions: Привязка Stream ID -> Leg ID
stream_bindings: Arc<DashMap<u32, u32>>,
id_gen: Arc<IdGenerator>,
session_id: Arc<String>,
}
@@ -66,6 +66,7 @@ impl Muxer {
Self {
legs: Arc::new(DashMap::new()),
streams: Arc::new(DashMap::new()),
stream_bindings: Arc::new(DashMap::new()),
id_gen: Arc::new(IdGenerator::new(is_client)),
session_id: Arc::new(session_id),
}
@@ -77,7 +78,6 @@ impl Muxer {
control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>,
) {
// 👈 Используем константу вместо магической 10
if self.legs.len() >= MAX_TUNNEL_LEGS as usize {
warn!(leg_id, "MUXER: Max legs reached: {}", MAX_TUNNEL_LEGS);
return;
@@ -96,7 +96,10 @@ impl Muxer {
pub fn remove_leg(&self, leg_id: u32) {
self.legs.remove(&leg_id);
info!(leg_id, "MUXER: Leg removed");
// Удаляем все привязки стримов к этой ноге, чтобы они перебалансировались
self.stream_bindings
.retain(|_, target_leg| *target_leg != leg_id);
info!(leg_id, "MUXER: Leg removed and bindings cleared");
}
pub fn active_legs_count(&self) -> usize {
@@ -108,15 +111,26 @@ impl Muxer {
return None;
}
// 1. Ищем существующую привязку (Sticky Session)
if let Some(leg_id_ref) = self.stream_bindings.get(&stream_id) {
let leg_id = *leg_id_ref;
if let Some(leg) = self.legs.get(&leg_id) {
return Some((leg_id, leg.clone()));
}
}
// 2. Если привязки нет — выбираем новую ногу
let is_udp = matches!(frame_type, FrameType::UdpData | FrameType::UdpConnect);
let mut candidates: Vec<(u32, MuxLeg)> = self
.legs
.iter()
.map(|kv| (*kv.key(), kv.value().clone()))
// Фильтруем по типу: четные ID — TCP, нечетные — UDP
.filter(|(id, _)| if is_udp { id % 2 != 0 } else { id % 2 == 0 })
.collect();
// Fallback: если подходящих по типу ног нет, берем любые
if candidates.is_empty() {
candidates = self
.legs
@@ -125,7 +139,7 @@ impl Muxer {
.collect();
}
// Выбираем ноги с наименьшим RTT (ноги со страйками = 5000ms будут в конце)
// Сортируем по RTT (Health Check записывает сюда данные)
candidates.sort_by_key(|(_, leg)| {
let rtt = leg.stats.rtt_ms.load(Ordering::Relaxed);
if rtt == 0 {
@@ -135,11 +149,18 @@ impl Muxer {
}
});
// 👈 Используем константу
let pool_size = std::cmp::min(candidates.len(), MUXER_POOL_SIZE);
let target = &candidates[stream_id as usize % pool_size];
if pool_size == 0 {
return None;
}
Some(target.clone())
// Выбираем из топа лучших
let (selected_id, selected_leg) = candidates[stream_id as usize % pool_size].clone();
// Запоминаем выбор для этого стрима
self.stream_bindings.insert(stream_id, selected_id);
Some((selected_id, selected_leg))
}
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
@@ -150,6 +171,9 @@ impl Muxer {
.select_leg(&message.frame_type, stream_id)
.ok_or_else(|| "MUXER: No active legs available".to_string())?;
// Используем обычный .await для всех типов кадров.
// Это гарантирует, что пакет попадет в очередь ноги, но может притормозить цикл Engine,
// если очередь физического соединения переполнена.
let target_tx = match message.frame_type {
FrameType::Connect
| FrameType::Close
@@ -158,20 +182,18 @@ impl Muxer {
_ => leg.data_tx.clone(),
};
match target_tx.send(message).await {
Ok(_) => {
leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed);
if let Some(stream_ref) = self.streams.get(&stream_id) {
stream_ref.1.tx_bytes.fetch_add(size, Ordering::Relaxed);
}
Ok(())
}
Err(_) => {
self.remove_leg(leg_id);
Err(format!("MUXER: Leg {} died during send", leg_id))
}
if let Err(_) = target_tx.send(message).await {
self.remove_leg(leg_id);
return Err(format!("MUXER: Leg {} died during send", leg_id));
}
// Обновляем статистику
leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed);
if let Some(stream_ref) = self.streams.get(&stream_id) {
stream_ref.1.tx_bytes.fetch_add(size, Ordering::Relaxed);
}
Ok(())
}
pub async fn send_data_safe(
@@ -215,6 +237,8 @@ impl Muxer {
pub fn remove_stream(&self, stream_id: u32) {
self.streams.remove(&stream_id);
self.stream_bindings.remove(&stream_id); // Чистим привязку
trace!(stream_id, "MUXER: Stream and bindings removed");
}
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
@@ -247,7 +271,6 @@ impl Muxer {
self.id_gen.next()
}
// 👈 Полностью переписанный параллельный Health Check со страйками
pub async fn perform_health_check(&self) {
let legs: Vec<(u32, Sender<MuxMessage>)> = self
.legs
@@ -255,67 +278,38 @@ impl Muxer {
.map(|k| (*k.key(), k.value().control_tx.clone()))
.collect();
// Запускаем проверку каждой ноги параллельно
for (leg_id, tx) in legs {
let muxer = self.clone(); // Дешевое клонирование (внутри Arc)
let probe_stream_id = self.id_gen.next();
let (probe_tx, mut probe_rx) = tokio::sync::mpsc::channel(2);
tokio::spawn(async move {
let probe_stream_id = muxer.id_gen.next();
let (probe_tx, mut probe_rx) = tokio::sync::mpsc::channel(2);
self.register_stream(probe_stream_id, probe_tx);
muxer.register_stream(probe_stream_id, probe_tx);
let msg = MuxMessage {
stream_id: probe_stream_id,
frame_type: FrameType::Handshake,
data: Bytes::from("PING"),
};
let msg = MuxMessage {
stream_id: probe_stream_id,
frame_type: FrameType::Handshake,
data: Bytes::from("PING"),
};
let start = std::time::Instant::now();
let start = std::time::Instant::now();
if tx.try_send(msg).is_ok() {
// Используем новую константу таймаута
match tokio::time::timeout(HEALTH_CHECK_TIMEOUT, probe_rx.recv()).await {
Ok(Some(_)) => {
let rtt = start.elapsed().as_millis() as u32;
if let Some(leg) = muxer.legs.get(&leg_id) {
// Пинг прошел: обновляем RTT и СБРАСЫВАЕМ страйки
leg.stats.rtt_ms.store(rtt, Ordering::Relaxed);
leg.stats.strikes.store(0, Ordering::Relaxed);
debug!(leg_id, rtt, "✅ Leg Health Check OK");
}
}
_ => {
// Пинг провален: увеличиваем страйки
if let Some(leg) = muxer.legs.get(&leg_id) {
let strikes = leg.stats.strikes.fetch_add(1, Ordering::Relaxed) + 1;
if strikes >= 3 {
// 3 промаха = смерть. Балансировщик перестанет кидать сюда трафик
leg.stats.rtt_ms.store(5000, Ordering::Relaxed);
warn!(
leg_id,
strikes, "❌ Leg DEAD (3 strikes). RTT set to 5000."
);
} else {
// 1-2 промаха: ждем, возможно это радио-пауза в мобильной сети
warn!(
leg_id,
strikes, "⚠️ Leg missed ping. Strike {}/3", strikes
);
}
}
if tx.try_send(msg).is_ok() {
match tokio::time::timeout(HEALTH_CHECK_TIMEOUT, probe_rx.recv()).await {
Ok(Some(_)) => {
let rtt = start.elapsed().as_millis() as u32;
if let Some(leg) = self.legs.get(&leg_id) {
leg.stats.rtt_ms.store(rtt, Ordering::Relaxed);
debug!(leg_id, rtt, "✅ Leg Health Check OK");
}
}
} else {
// Если канал (mpsc) забит или закрыт, считаем ногу мертвой сразу
if let Some(leg) = muxer.legs.get(&leg_id) {
leg.stats.rtt_ms.store(5000, Ordering::Relaxed);
_ => {
if let Some(leg) = self.legs.get(&leg_id) {
leg.stats.rtt_ms.store(5000, Ordering::Relaxed);
warn!(leg_id, "❌ Leg Health Check Timeout");
}
}
}
muxer.remove_stream(probe_stream_id);
});
}
self.remove_stream(probe_stream_id);
}
}
@@ -350,7 +344,6 @@ impl Muxer {
let tx = stats.tx_bytes.load(Ordering::Relaxed);
let rx = stats.rx_bytes.load(Ordering::Relaxed);
let rtt = stats.rtt_ms.load(Ordering::Relaxed);
let strikes = stats.strikes.load(Ordering::Relaxed);
total_tx += tx;
total_rx += rx;
@@ -362,23 +355,13 @@ impl Muxer {
format!("{}ms", rtt)
};
// 👈 Добавлен визуальный статус ноги для отладки
let status_icon = if strikes >= 3 {
"💀"
} else if strikes > 0 {
"⚠️"
} else {
""
};
legs_info.push(format!(
" ├─ Leg {} ({}) ─ ⇡ {:<9} | ⇣ {:<9} [RTT: {:<6}] [Status: {}]",
" ├─ Leg {} ({}) ─ ⇡ {:<9} | ⇣ {:<9} [RTT: {}]",
id,
leg_type,
Self::format_size(tx),
Self::format_size(rx),
rtt_str,
status_icon
rtt_str
));
}
+2 -2
View File
@@ -10,8 +10,8 @@ pub const TCP_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(20); // Врем
pub const UDP_IDLE_TIMEOUT: Duration = Duration::from_secs(60); // Смерть UDP сессии без данных
pub const GLOBAL_IDLE_TIMEOUT: Duration = Duration::from_secs(120); // Очистка Tracker-ом
pub const HEALTH_CHECK_INTERVAL: Duration = Duration::from_secs(5); // Частота пинга Leg-ов
pub const HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(5);
pub const LEG_RECONNECT_DELAY: Duration = Duration::from_secs(5); // Пауза перед реконнектом Leg
pub const HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(15);
pub const LEG_RECONNECT_DELAY: Duration = Duration::from_secs(30); // Пауза перед реконнектом Leg
pub const BRIDGE_IDLE_TIMEOUT: Duration = Duration::from_secs(300); // Таймаут задач-бриджей
// --- Сетевые порты ---