Files
Meshray-Manager/core/connect/strategy.go
T
2026-06-30 15:14:37 +08:00

755 lines
19 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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-FakeTCPUDP 封装 TCP 头部,欺骗防火墙)
LayerFakeTCP
// LayerRealTCP Direct-RealTCPP2P 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 DataChannelDTLS 加密)
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
}