面板增加评分系统
┌──────────────────────────────────────────────────┐
│ forward_panel_desktop.py 🖥️ │
│ ┌─────────────────┐ ┌────────────────────┐ │
│ │ 原有故障转移模式 │◄──►│ 🧠 评分调度模式 │ │
│ │ (failover) │切换│ (scoring) │ │
│ │ ✅ 完全保留 │ │ 新增, 可插拔 │ │
│ └─────────────────┘ └────────┬───────────┘ │
│ │ │
│ ┌──────────────▼───────────┐ │
│ │ node_scorer.py 📦 │ │
│ │ NodeScoreScheduler │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ ScoringConfig │ │ │
│ │ │ NodeScore (per-node) │ │ │
│ │ │ NodeState Enum │ │ │
│ │ └─────────────────────┘ │ │
│ └──────────────────────────┘ │
└──────────────────────────────────────────────────┘
— 节点评分主表
CREATE TABLE node_scores (
node_key TEXT PRIMARY KEY, — ‘ip:port’ 唯一标识
name TEXT DEFAULT ”, — 显示名称 (A,B,C…)
ip TEXT NOT NULL,
port INTEGER DEFAULT 0,
score REAL DEFAULT 50.0, — ★ 核心: 0-100实时评分
state TEXT DEFAULT ‘active’, — active/cooldown/blacklist
success_count INTEGER DEFAULT 0, — 累计成功次数
fail_count INTEGER DEFAULT 0, — 累计失败次数
consecutive_successes INTEGER DEFAULT 0, — 连击成功(加分)
consecutive_fails INTEGER DEFAULT 0, — 连击失败(惩罚)
avg_latency_ms REAL DEFAULT 0.0, — EMA平滑平均延迟
last_latency_ms REAL DEFAULT 0.0, — 最近延迟
last_check_time REAL DEFAULT 0.0, — 上次检测时间戳
last_state_change REAL DEFAULT 0.0, — 状态变更时间
cooldown_until REAL DEFAULT 0.0 — 冷却到期时间
);
— 评分历史表(选配,趋势分析)
CREATE TABLE node_score_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
node_key TEXT, score REAL, state TEXT,
event TEXT, — ‘success’ / ‘fail’ / ‘state_change’
latency_ms REAL, recorded_at REAL
);
def _calc_success_adjustment(node, latency_ms) -> float:
“””成功时评分调整”””
adjustment = +5.0 # 基础成功加分
if latency_ms < 50: adjustment += +3.0 # 极低延迟奖励
elif latency_ms < 100: adjustment += +1.0 # 低延迟奖励
elif latency_ms >= 200: adjustment += -2.0 # 高延迟惩罚
elif latency_ms >= 500: adjustment += -5.0 # 极高延迟惩罚
if node.consecutive_successes > 10: adjustment += +3.0 # 超长连击
elif node.consecutive_successes > 5: adjustment += +2.0 # 连击奖励
return adjustment
def _calc_failure_adjustment(node) -> float:
“””失败时评分调整”””
adjustment = -15.0 # 基础失败扣分
if node.consecutive_fails >= 3: # 连续失败阈值
adjustment += -10.0 # 额外连败惩罚
return adjustment
最终评分: score = clamp(score + adjustment, 0, 100)
def select_node() -> (node_key, NodeScore):
# 1. 过滤: 排除黑名单节点
eligible = [n for n in nodes if n.state != BLACKLIST]
# 2. 分层: 优先活跃节点, 无活跃时才用冷却节点
candidates = active_nodes if active_nodes else cooldown_nodes
# 3. 权重计算:
# active: weight = (score / 100)²
# cooldown: weight = (score / 100)² × 0.2
# blacklist: weight = 0 (被过滤)
# 4. 加权随机选择: random.choices(candidates, weights=weights)
return chosen_node
score<20 ┌──────────┐
┌────────────────────────────►│ COOLDOWN │
│ │ (降权0.2) │
│ └─────┬─────┘
│ cooldown过期(60s) │ score<5
│ + score>30 │
│ ┌──────────┐ ┌──────▼──────┐
└──│ ACTIVE │◄─────────────│ BLACKLIST │
│ (全权) │ 探活成功 │ (排除调度) │
└──────────┘ 恢复分=30 └──────┬──────┘
▲ │
│ 定时探活(5分钟) │
└───────────────────────────┘
┌─────────────────────────────────────────┐
│ ⚙️ 检测设置 │
│ │
│ 检测间隔(秒): [30] 失败阈值(次): [3] │
│ 定时切换(分钟):[0 ] 分钟 (0=关闭) │
│ 流量切换(GB): [0 ] GB (0=关闭) │
│ 黑名单探活(秒):[60] 秒 (10-3600) │
│ │
│ ☑ 🧠 节点智能评分调度 │ ← 勾上这个!
│ 基于评分(0-100)+加权随机替代故障转移 │
│ │
│ [▶ 启动] [⏹ 停止] │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 🧠 评分调度 – A | 均分71 活跃3 冷却0 黑名单2 │ ← 根节点汇总
├─────────────────────────────────────────────────────┤
│ ▶ A 🟢88分 │ 10✓/0✗ ⏱42ms ⚖35% │ ← 当前活跃(绿底高亮)
│ B 🔴30分 │ 6✓/4✗ ⏱73ms ⚖9% │ ← 活跃-良好(浅青绿)
│ C 🟡18分 │ 3✓/5✗ ⏱342ms ⚖降权4% 冷却47s │ ← 冷却中(橙底)
│ D 🔴5分 │ 1✓/8✗ ⏱- ⚖排除 │ ← 黑名单(红底)
│ E 🟢95分 │ 10✓/0✗ ⏱49ms ⚖34% │ ← 活跃-优秀(浅青绿)
└─────────────────────────────────────────────────────┘