Initial commit
This commit is contained in:
@@ -0,0 +1,754 @@
|
||||
package connect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Layer 传输层类型(9 层策略)
|
||||
type Layer int
|
||||
|
||||
const (
|
||||
// LayerDirectUDP Direct-UDP 直连(WireGuard over UDP)- 最高效
|
||||
LayerDirectUDP Layer = iota
|
||||
|
||||
// LayerFakeTCP Direct-FakeTCP(UDP 封装 TCP 头部,欺骗防火墙)
|
||||
LayerFakeTCP
|
||||
|
||||
// LayerRealTCP Direct-RealTCP(P2P TCP 直连)
|
||||
LayerRealTCP
|
||||
|
||||
// LayerTURNUDP TURN-UDP 中继(标准 RFC 5766)
|
||||
LayerTURNUDP
|
||||
|
||||
// LayerTURNQUIC TURN-QUIC 中继(私有扩展,RFC 9000)
|
||||
LayerTURNQUIC
|
||||
|
||||
// LayerTURNTCP TURN-TCP 中继(TCP 中继)
|
||||
LayerTURNTCP
|
||||
|
||||
// LayerTURNTLS TURN-TLS 中继(TLS 加密,RFC 8656)
|
||||
LayerTURNTLS
|
||||
|
||||
// LayerWebRTC WebRTC DataChannel(DTLS 加密)
|
||||
LayerWebRTC
|
||||
|
||||
// LayerWS WS/WSS 兜底(仅 80/443 端口,终极兜底)
|
||||
LayerWS
|
||||
|
||||
// LayerCount 传输层总数
|
||||
LayerCount
|
||||
)
|
||||
|
||||
// String 实现 Stringer 接口
|
||||
func (l Layer) String() string {
|
||||
switch l {
|
||||
case LayerDirectUDP:
|
||||
return "Direct-UDP"
|
||||
case LayerFakeTCP:
|
||||
return "Direct-FakeTCP"
|
||||
case LayerRealTCP:
|
||||
return "Direct-RealTCP"
|
||||
case LayerTURNUDP:
|
||||
return "TURN-UDP"
|
||||
case LayerTURNQUIC:
|
||||
return "TURN-QUIC"
|
||||
case LayerTURNTCP:
|
||||
return "TURN-TCP"
|
||||
case LayerTURNTLS:
|
||||
return "TURN-TLS"
|
||||
case LayerWebRTC:
|
||||
return "WebRTC"
|
||||
case LayerWS:
|
||||
return "WS/WSS"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultLayerOrder 默认优先级顺序(从最优到兜底)
|
||||
// 根据 MeshRay_项目文档 v2.0.1 第 132-153 行定义
|
||||
var DefaultLayerOrder = []Layer{
|
||||
LayerDirectUDP, // 1. Direct-UDP - 公网/锥型 NAT,首选链路
|
||||
LayerFakeTCP, // 2. Direct-FakeTCP - 校园网、酒店 Wi-Fi、UDP 被 QoS 限速
|
||||
LayerRealTCP, // 3. Direct-RealTCP - 完全禁用 UDP,仅允许 TCP 出站
|
||||
LayerTURNUDP, // 4. TURN-UDP 中继 - 无 P2P 直连,但 UDP 可通
|
||||
LayerTURNQUIC, // 5. TURN-QUIC 中继 - UDP 可通但弱网(4G/5G、高丢包)【私有扩展】
|
||||
LayerTURNTCP, // 6. TURN-TCP 中继 - UDP 封禁,仅放行 TCP
|
||||
LayerTURNTLS, // 7. TURN-TLS 中继 - 企业防火墙 DPI,仅放行 HTTPS
|
||||
LayerWebRTC, // 8. WebRTC 终极兜底 - 最严格隔离内网、代理环境
|
||||
LayerWS, // 9. WS/WSS 兜底 - 仅放行 80/443 端口,且封锁 TURN
|
||||
}
|
||||
|
||||
// TransportFactory 传输工厂接口 - 每种传输方式必须实现
|
||||
type TransportFactory interface {
|
||||
// Layer 返回传输层类型
|
||||
Layer() Layer
|
||||
|
||||
// Dial 建立连接到对端
|
||||
// 返回标准的 net.Conn 接口
|
||||
Dial(ctx context.Context, config *DialConfig) (net.Conn, error)
|
||||
|
||||
// Name 返回传输方式名称(用于日志)
|
||||
Name() string
|
||||
}
|
||||
|
||||
// DialConfig 拨号配置
|
||||
type DialConfig struct {
|
||||
// PeerID 对端标识
|
||||
PeerID string
|
||||
|
||||
// PeerPublicKey 对端公钥
|
||||
PeerPublicKey string
|
||||
|
||||
// STUNServers STUN 服务器列表(用于 P2P)
|
||||
STUNServers []string
|
||||
|
||||
// TURNServers TURN 服务器列表
|
||||
TURNServers []string
|
||||
|
||||
// WSServers WebSocket 服务器列表
|
||||
WSServers []string
|
||||
|
||||
// SignalingServers WebRTC 第三方信令服务器列表
|
||||
SignalingServers []string
|
||||
|
||||
// ICESServers ICE 服务器列表(STUN+TURN 的组合)
|
||||
ICESServers []string
|
||||
|
||||
// Timeout 连接超时
|
||||
Timeout time.Duration
|
||||
|
||||
// Logger 日志记录器
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
// StrategyScheduler 9 层策略调度器(主动调度层)
|
||||
// 职责:
|
||||
// 1. 按优先级选择链路(P2P → Mesh中继 → TURN-UDP → ... → WS/WSS)
|
||||
// 2. 根据网络环境自动切换(500ms 超时 / 10s 丢包率 > 10%)
|
||||
// 3. 切换后探测恢复并自动切回高性能链路(30s)
|
||||
type StrategyScheduler struct {
|
||||
layerFactories map[Layer]TransportFactory // 各层的工厂
|
||||
layerOrder []Layer // 优先级顺序
|
||||
logger *zap.Logger
|
||||
|
||||
// 每个 Peer 的降级控制器
|
||||
fallbackControllers map[string]*FallbackController // peerID -> controller
|
||||
fallbackMu sync.RWMutex
|
||||
|
||||
// 当前活跃连接
|
||||
activeConnections map[string]activeConn // peerID -> 连接信息
|
||||
connMu sync.RWMutex
|
||||
|
||||
// 统计
|
||||
stats *SchedulerStats
|
||||
|
||||
// 连接变更回调(通知上层 ConnManager)
|
||||
OnConnectionUpdate func(peerID string, conn net.Conn, err error)
|
||||
}
|
||||
|
||||
// activeConn 活跃连接信息
|
||||
type activeConn struct {
|
||||
conn net.Conn
|
||||
layer Layer
|
||||
peerID string
|
||||
established time.Time
|
||||
config *DialConfig
|
||||
}
|
||||
|
||||
// SchedulerStats 调度器统计
|
||||
type SchedulerStats struct {
|
||||
mu sync.RWMutex
|
||||
totalDials int64
|
||||
successDials int64
|
||||
fallbackCount int64
|
||||
recoveryCount int64
|
||||
layerDialCount map[Layer]int64
|
||||
layerFailCount map[Layer]int64
|
||||
}
|
||||
|
||||
// NewStrategyScheduler 创建策略调度器
|
||||
func NewStrategyScheduler(logger *zap.Logger) *StrategyScheduler {
|
||||
return &StrategyScheduler{
|
||||
layerFactories: make(map[Layer]TransportFactory),
|
||||
layerOrder: DefaultLayerOrder,
|
||||
logger: logger,
|
||||
fallbackControllers: make(map[string]*FallbackController),
|
||||
activeConnections: make(map[string]activeConn),
|
||||
stats: &SchedulerStats{
|
||||
layerDialCount: make(map[Layer]int64),
|
||||
layerFailCount: make(map[Layer]int64),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterFactory 注册传输工厂
|
||||
func (s *StrategyScheduler) RegisterFactory(factory TransportFactory) {
|
||||
layer := factory.Layer()
|
||||
s.layerFactories[layer] = factory
|
||||
s.logger.Debug("注册传输工厂",
|
||||
zap.String("layer", layer.String()),
|
||||
zap.String("name", factory.Name()))
|
||||
}
|
||||
|
||||
// SetLayerOrder 设置优先级顺序
|
||||
func (s *StrategyScheduler) SetLayerOrder(order []Layer) {
|
||||
if len(order) == 0 {
|
||||
s.logger.Warn("空的层级顺序,使用默认顺序")
|
||||
return
|
||||
}
|
||||
s.layerOrder = order
|
||||
s.logger.Info("更新传输层优先级顺序", zap.Any("order", order))
|
||||
}
|
||||
|
||||
// Dial 按优先级顺序尝试建立连接
|
||||
// 这是核心方法,实现了 9 层策略调度
|
||||
func (s *StrategyScheduler) Dial(config *DialConfig) (net.Conn, error) {
|
||||
ctx := context.Background()
|
||||
if config.Timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, config.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
s.logger.Info("开始 8 层策略调度连接",
|
||||
zap.String("peer_id", config.PeerID),
|
||||
zap.Int("total_layers", len(s.layerOrder)))
|
||||
|
||||
// 统计
|
||||
s.stats.mu.Lock()
|
||||
s.stats.totalDials++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
var lastErr error
|
||||
for i, layer := range s.layerOrder {
|
||||
factory, ok := s.layerFactories[layer]
|
||||
if !ok {
|
||||
s.logger.Debug("该传输层未注册,跳过",
|
||||
zap.String("layer", layer.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
s.logger.Debug("尝试第 N 层传输",
|
||||
zap.Int("index", i),
|
||||
zap.String("layer", layer.String()),
|
||||
zap.String("name", factory.Name()))
|
||||
|
||||
// 统计该层拨号次数
|
||||
s.stats.mu.Lock()
|
||||
s.stats.layerDialCount[layer]++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
startTime := time.Now()
|
||||
conn, err := factory.Dial(ctx, config)
|
||||
duration := time.Since(startTime)
|
||||
|
||||
if err == nil {
|
||||
// 成功!
|
||||
s.stats.mu.Lock()
|
||||
s.stats.successDials++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
// 记录活跃连接
|
||||
s.connMu.Lock()
|
||||
s.activeConnections[config.PeerID] = activeConn{
|
||||
conn: conn,
|
||||
layer: layer,
|
||||
peerID: config.PeerID,
|
||||
established: time.Now(),
|
||||
config: config,
|
||||
}
|
||||
s.connMu.Unlock()
|
||||
|
||||
// 创建或更新降级控制器
|
||||
s.ensureFallbackController(config.PeerID, layer)
|
||||
|
||||
s.logger.Info("连接建立成功",
|
||||
zap.String("layer", layer.String()),
|
||||
zap.String("name", factory.Name()),
|
||||
zap.String("peer_id", config.PeerID),
|
||||
zap.String("remote_addr", conn.RemoteAddr().String()),
|
||||
zap.Duration("duration", duration))
|
||||
|
||||
// 包装连接,用于监控
|
||||
return newMonitoredConn(conn, config.PeerID, layer, s), nil
|
||||
}
|
||||
|
||||
// 失败,统计
|
||||
s.stats.mu.Lock()
|
||||
s.stats.layerFailCount[layer]++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
// 记录失败并继续尝试下一层
|
||||
lastErr = err
|
||||
s.logger.Warn("该传输层连接失败,尝试下一层",
|
||||
zap.String("layer", layer.String()),
|
||||
zap.Duration("duration", duration),
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
// 所有层都失败
|
||||
return nil, fmt.Errorf("所有传输层均失败,最后错误:%w", lastErr)
|
||||
}
|
||||
|
||||
// reconnectToLayer 触发重连到指定层级
|
||||
func (s *StrategyScheduler) reconnectToLayer(peerID string, toLayer Layer) {
|
||||
s.connMu.RLock()
|
||||
ac, exists := s.activeConnections[peerID]
|
||||
s.connMu.RUnlock()
|
||||
|
||||
if !exists || ac.config == nil {
|
||||
s.logger.Warn("重连失败:找不到活跃连接配置", zap.String("peer_id", peerID))
|
||||
return
|
||||
}
|
||||
|
||||
factory, ok := s.layerFactories[toLayer]
|
||||
if !ok {
|
||||
s.logger.Error("重连失败:找不到目标层级工厂", zap.String("layer", toLayer.String()))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := factory.Dial(ctx, ac.config)
|
||||
if err != nil {
|
||||
s.logger.Error("降级重连失败", zap.Error(err))
|
||||
if s.OnConnectionUpdate != nil {
|
||||
s.OnConnectionUpdate(peerID, nil, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wrappedConn := newMonitoredConn(conn, peerID, toLayer, s)
|
||||
|
||||
s.connMu.Lock()
|
||||
if oldAc, exists := s.activeConnections[peerID]; exists {
|
||||
oldAc.conn.Close()
|
||||
}
|
||||
s.activeConnections[peerID] = activeConn{
|
||||
conn: wrappedConn,
|
||||
layer: toLayer,
|
||||
peerID: peerID,
|
||||
established: time.Now(),
|
||||
config: ac.config,
|
||||
}
|
||||
s.connMu.Unlock()
|
||||
|
||||
if s.OnConnectionUpdate != nil {
|
||||
s.OnConnectionUpdate(peerID, wrappedConn, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// ensureFallbackController 确保对端有降级控制器
|
||||
func (s *StrategyScheduler) ensureFallbackController(peerID string, initialLayer Layer) {
|
||||
s.fallbackMu.Lock()
|
||||
defer s.fallbackMu.Unlock()
|
||||
|
||||
if _, exists := s.fallbackControllers[peerID]; !exists {
|
||||
controller := NewFallbackController(
|
||||
initialLayer,
|
||||
func(from, to Layer) {
|
||||
// 降级回调
|
||||
s.stats.mu.Lock()
|
||||
s.stats.fallbackCount++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
s.logger.Warn("链路降级",
|
||||
zap.String("peer_id", peerID),
|
||||
zap.String("from_layer", from.String()),
|
||||
zap.String("to_layer", to.String()))
|
||||
|
||||
// 触发重连到新层级
|
||||
go s.reconnectToLayer(peerID, to)
|
||||
},
|
||||
func(to Layer) {
|
||||
// 恢复回调
|
||||
s.stats.mu.Lock()
|
||||
s.stats.recoveryCount++
|
||||
s.stats.mu.Unlock()
|
||||
|
||||
s.logger.Info("链路恢复",
|
||||
zap.String("peer_id", peerID),
|
||||
zap.String("to_layer", to.String()))
|
||||
|
||||
// 回调处理已经在 probeHighLayers 中完成并传递了新连接
|
||||
},
|
||||
s.logger,
|
||||
s, // pass scheduler to access activeConnections
|
||||
)
|
||||
s.fallbackControllers[peerID] = controller
|
||||
}
|
||||
}
|
||||
|
||||
// RecordLatency 记录延迟(供 MonitoredConn 调用)
|
||||
func (s *StrategyScheduler) RecordLatency(peerID string, success bool, duration time.Duration) {
|
||||
s.fallbackMu.RLock()
|
||||
controller, exists := s.fallbackControllers[peerID]
|
||||
s.fallbackMu.RUnlock()
|
||||
|
||||
if exists {
|
||||
controller.CheckAndFallback(success, duration)
|
||||
}
|
||||
}
|
||||
|
||||
// GetActiveLayer 获取当前活跃的传输层(用于监控)
|
||||
func (s *StrategyScheduler) GetActiveLayer() Layer {
|
||||
// 返回第一个活跃连接的层级
|
||||
s.connMu.RLock()
|
||||
defer s.connMu.RUnlock()
|
||||
|
||||
for _, ac := range s.activeConnections {
|
||||
return ac.layer
|
||||
}
|
||||
return LayerDirectUDP // 默认值
|
||||
}
|
||||
|
||||
// GetAllActiveLayers 获取所有 Peer 的活跃层级(用于全局监控)
|
||||
func (s *StrategyScheduler) GetAllActiveLayers() map[string]Layer {
|
||||
s.connMu.RLock()
|
||||
defer s.connMu.RUnlock()
|
||||
|
||||
result := make(map[string]Layer)
|
||||
for peerID, ac := range s.activeConnections {
|
||||
result[peerID] = ac.layer
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPeerLayer 获取指定 Peer 的当前层级
|
||||
func (s *StrategyScheduler) GetPeerLayer(peerID string) Layer {
|
||||
s.connMu.RLock()
|
||||
defer s.connMu.RUnlock()
|
||||
|
||||
if ac, exists := s.activeConnections[peerID]; exists {
|
||||
return ac.layer
|
||||
}
|
||||
return LayerDirectUDP // 默认值
|
||||
}
|
||||
|
||||
// GetStats 获取统计信息
|
||||
func (s *StrategyScheduler) GetStats() map[string]interface{} {
|
||||
s.stats.mu.RLock()
|
||||
defer s.stats.mu.RUnlock()
|
||||
|
||||
layerStats := make(map[string]int64)
|
||||
for layer, count := range s.stats.layerDialCount {
|
||||
layerStats[layer.String()+"_dial"] = count
|
||||
}
|
||||
for layer, count := range s.stats.layerFailCount {
|
||||
layerStats[layer.String()+"_fail"] = count
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_dials": s.stats.totalDials,
|
||||
"success_dials": s.stats.successDials,
|
||||
"fallback_count": s.stats.fallbackCount,
|
||||
"recovery_count": s.stats.recoveryCount,
|
||||
"layer_stats": layerStats,
|
||||
}
|
||||
}
|
||||
|
||||
// ClosePeer 关闭指定 Peer 的连接和控制器
|
||||
func (s *StrategyScheduler) ClosePeer(peerID string) {
|
||||
// 关闭连接
|
||||
s.connMu.Lock()
|
||||
if ac, exists := s.activeConnections[peerID]; exists {
|
||||
ac.conn.Close()
|
||||
delete(s.activeConnections, peerID)
|
||||
}
|
||||
s.connMu.Unlock()
|
||||
|
||||
// 移除降级控制器
|
||||
s.fallbackMu.Lock()
|
||||
if controller, exists := s.fallbackControllers[peerID]; exists {
|
||||
// 停止恢复探测器
|
||||
if controller.recoveryTimer != nil {
|
||||
controller.recoveryTimer.Stop()
|
||||
}
|
||||
delete(s.fallbackControllers, peerID)
|
||||
}
|
||||
s.fallbackMu.Unlock()
|
||||
|
||||
s.logger.Debug("已关闭 Peer 连接和控制器",
|
||||
zap.String("peer_id", peerID))
|
||||
}
|
||||
|
||||
// MonitoredConn 带监控的连接包装器
|
||||
type MonitoredConn struct {
|
||||
net.Conn
|
||||
peerID string
|
||||
layer Layer
|
||||
scheduler *StrategyScheduler
|
||||
}
|
||||
|
||||
// newMonitoredConn 创建带监控的连接
|
||||
func newMonitoredConn(conn net.Conn, peerID string, layer Layer, scheduler *StrategyScheduler) *MonitoredConn {
|
||||
return &MonitoredConn{
|
||||
Conn: conn,
|
||||
peerID: peerID,
|
||||
layer: layer,
|
||||
scheduler: scheduler,
|
||||
}
|
||||
}
|
||||
|
||||
// Read 重写 Read 方法,记录延迟
|
||||
func (c *MonitoredConn) Read(b []byte) (n int, err error) {
|
||||
start := time.Now()
|
||||
n, err = c.Conn.Read(b)
|
||||
duration := time.Since(start)
|
||||
|
||||
// 记录成功/失败
|
||||
c.scheduler.RecordLatency(c.peerID, err == nil, duration)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write 重写 Write 方法,记录延迟
|
||||
func (c *MonitoredConn) Write(b []byte) (n int, err error) {
|
||||
start := time.Now()
|
||||
n, err = c.Conn.Write(b)
|
||||
duration := time.Since(start)
|
||||
|
||||
// 记录成功/失败
|
||||
c.scheduler.RecordLatency(c.peerID, err == nil, duration)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// FallbackController 降级控制器
|
||||
type FallbackController struct {
|
||||
currentLayer Layer // 当前使用的层
|
||||
windowStart time.Time // 滑动窗口起始时间
|
||||
packetCount int // 总包数
|
||||
lostPacketCount int // 丢包数
|
||||
mu chan struct{} // 互斥锁(用 channel 实现)
|
||||
triggerFallback func(Layer, Layer) // 降级触发回调
|
||||
triggerRecovery func(Layer) // 恢复触发回调
|
||||
logger *zap.Logger
|
||||
recoveryTimer *time.Timer // 恢复探测定时器
|
||||
scheduler *StrategyScheduler
|
||||
}
|
||||
|
||||
const (
|
||||
// TimeoutThreshold 单次超时阈值
|
||||
TimeoutThreshold = 500 * time.Millisecond
|
||||
|
||||
// PacketLossThreshold 丢包率阈值
|
||||
PacketLossThreshold = 0.10 // 10%
|
||||
|
||||
// RecoveryInterval 恢复探测间隔
|
||||
RecoveryInterval = 30 * time.Second
|
||||
|
||||
// SlidingWindowDuration 滑动窗口时长
|
||||
SlidingWindowDuration = 10 * time.Second
|
||||
)
|
||||
|
||||
// NewFallbackController 创建降级控制器
|
||||
func NewFallbackController(
|
||||
initialLayer Layer,
|
||||
onFallback func(Layer, Layer),
|
||||
onRecovery func(Layer),
|
||||
logger *zap.Logger,
|
||||
scheduler *StrategyScheduler,
|
||||
) *FallbackController {
|
||||
fc := &FallbackController{
|
||||
currentLayer: initialLayer,
|
||||
mu: make(chan struct{}, 1),
|
||||
triggerFallback: onFallback,
|
||||
triggerRecovery: onRecovery,
|
||||
logger: logger,
|
||||
scheduler: scheduler,
|
||||
}
|
||||
|
||||
// 启动恢复探测
|
||||
fc.startRecoveryProbe()
|
||||
|
||||
return fc
|
||||
}
|
||||
|
||||
// CheckAndFallback 检查是否需要降级
|
||||
// 在每次连接操作后调用
|
||||
func (fc *FallbackController) CheckAndFallback(success bool, duration time.Duration) {
|
||||
select {
|
||||
case fc.mu <- struct{}{}:
|
||||
defer func() { <-fc.mu }()
|
||||
default:
|
||||
// 锁被占用,说明正在处理,直接返回
|
||||
return
|
||||
}
|
||||
|
||||
// 重置滑动窗口
|
||||
if time.Since(fc.windowStart) > SlidingWindowDuration {
|
||||
fc.windowStart = time.Now()
|
||||
fc.packetCount = 0
|
||||
fc.lostPacketCount = 0
|
||||
}
|
||||
|
||||
// 统计
|
||||
fc.packetCount++
|
||||
if !success || duration > TimeoutThreshold {
|
||||
fc.lostPacketCount++
|
||||
}
|
||||
|
||||
// 检查是否达到阈值
|
||||
if fc.packetCount >= 10 {
|
||||
lossRate := float64(fc.lostPacketCount) / float64(fc.packetCount)
|
||||
if lossRate > PacketLossThreshold {
|
||||
fc.triggerFallbackLocked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// triggerFallbackLocked 执行降级(已持有锁)
|
||||
func (fc *FallbackController) triggerFallbackLocked() {
|
||||
currentIndex := int(fc.currentLayer)
|
||||
if currentIndex >= int(LayerCount)-1 {
|
||||
// 已经是最低优先级,无法降级
|
||||
fc.logger.Warn("已是最底层级,无法降级",
|
||||
zap.String("current_layer", fc.currentLayer.String()))
|
||||
return
|
||||
}
|
||||
|
||||
nextLayer := Layer(currentIndex + 1)
|
||||
|
||||
// 在更新 currentLayer 之前保存旧值用于回调
|
||||
oldLayer := fc.currentLayer
|
||||
|
||||
fc.logger.Warn("触发降级",
|
||||
zap.String("from_layer", oldLayer.String()),
|
||||
zap.String("to_layer", nextLayer.String()))
|
||||
|
||||
fc.currentLayer = nextLayer
|
||||
fc.resetWindow()
|
||||
|
||||
if fc.triggerFallback != nil {
|
||||
fc.triggerFallback(oldLayer, nextLayer)
|
||||
}
|
||||
|
||||
// 重置恢复定时器
|
||||
fc.startRecoveryProbe()
|
||||
}
|
||||
|
||||
// startRecoveryProbe 启动恢复探测
|
||||
func (fc *FallbackController) startRecoveryProbe() {
|
||||
if fc.recoveryTimer != nil {
|
||||
fc.recoveryTimer.Stop()
|
||||
}
|
||||
|
||||
fc.recoveryTimer = time.AfterFunc(RecoveryInterval, func() {
|
||||
fc.probeHigherLayers()
|
||||
})
|
||||
}
|
||||
|
||||
// probeHigherLayers 探测更高层级
|
||||
func (fc *FallbackController) probeHigherLayers() {
|
||||
select {
|
||||
case fc.mu <- struct{}{}:
|
||||
defer func() { <-fc.mu }()
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
currentIndex := int(fc.currentLayer)
|
||||
if currentIndex == 0 {
|
||||
// 已经是最高优先级,无需探测
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试上一层
|
||||
higherLayer := Layer(currentIndex - 1)
|
||||
fc.logger.Info("探测更高层级",
|
||||
zap.String("current_layer", fc.currentLayer.String()),
|
||||
zap.String("probe_layer", higherLayer.String()))
|
||||
|
||||
// 获取 PeerID 及 Config
|
||||
fc.scheduler.connMu.RLock()
|
||||
var peerID string
|
||||
var config *DialConfig
|
||||
for pid, ac := range fc.scheduler.activeConnections {
|
||||
if ac.layer == fc.currentLayer {
|
||||
peerID = pid
|
||||
config = ac.config
|
||||
break
|
||||
}
|
||||
}
|
||||
fc.scheduler.connMu.RUnlock()
|
||||
|
||||
if config == nil {
|
||||
fc.logger.Warn("探测更高层级失败:找不到有效 DialConfig")
|
||||
return
|
||||
}
|
||||
|
||||
factory, ok := fc.scheduler.layerFactories[higherLayer]
|
||||
if !ok {
|
||||
fc.logger.Debug("更高层级未注册工厂,跳过探测")
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := factory.Dial(ctx, config)
|
||||
if err == nil {
|
||||
fc.logger.Info("更高层级探测成功,准备切换")
|
||||
|
||||
wrappedConn := newMonitoredConn(conn, peerID, higherLayer, fc.scheduler)
|
||||
|
||||
fc.scheduler.connMu.Lock()
|
||||
if ac, exists := fc.scheduler.activeConnections[peerID]; exists {
|
||||
ac.conn.Close() // Close old
|
||||
ac.conn = wrappedConn
|
||||
ac.layer = higherLayer
|
||||
fc.scheduler.activeConnections[peerID] = ac
|
||||
}
|
||||
fc.scheduler.connMu.Unlock()
|
||||
|
||||
if fc.scheduler.OnConnectionUpdate != nil {
|
||||
fc.scheduler.OnConnectionUpdate(peerID, wrappedConn, nil)
|
||||
}
|
||||
|
||||
// 触发恢复回调
|
||||
fc.triggerRecoveryLocked(higherLayer)
|
||||
} else {
|
||||
fc.logger.Debug("更高层级探测失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// triggerRecoveryLocked 执行恢复(已持有锁)
|
||||
func (fc *FallbackController) triggerRecoveryLocked(higherLayer Layer) {
|
||||
fc.logger.Info("触发恢复",
|
||||
zap.String("from_layer", fc.currentLayer.String()),
|
||||
zap.String("to_layer", higherLayer.String()))
|
||||
|
||||
fc.currentLayer = higherLayer
|
||||
fc.resetWindow()
|
||||
|
||||
if fc.triggerRecovery != nil {
|
||||
fc.triggerRecovery(higherLayer)
|
||||
}
|
||||
}
|
||||
|
||||
// resetWindow 重置滑动窗口
|
||||
func (fc *FallbackController) resetWindow() {
|
||||
fc.windowStart = time.Now()
|
||||
fc.packetCount = 0
|
||||
fc.lostPacketCount = 0
|
||||
}
|
||||
|
||||
// GetCurrentLayer 获取当前层级
|
||||
func (fc *FallbackController) GetCurrentLayer() Layer {
|
||||
select {
|
||||
case fc.mu <- struct{}{}:
|
||||
defer func() { <-fc.mu }()
|
||||
default:
|
||||
return fc.currentLayer
|
||||
}
|
||||
return fc.currentLayer
|
||||
}
|
||||
Reference in New Issue
Block a user