dae/component/outbound/dialer/alive_dialer_set.go

227 lines
6.5 KiB
Go
Raw Normal View History

2023-01-23 18:54:21 +07:00
/*
* SPDX-License-Identifier: AGPL-3.0-only
2023-01-28 12:56:06 +07:00
* Copyright (c) since 2023, v2rayA Organization <team@v2raya.org>
2023-01-23 18:54:21 +07:00
*/
package dialer
import (
"github.com/mzz2017/softwind/pkg/fastrand"
"github.com/sirupsen/logrus"
2023-01-28 14:47:43 +07:00
"github.com/v2rayA/dae/common/consts"
2023-01-23 18:54:21 +07:00
"sync"
"time"
)
type minLatency struct {
latency time.Duration
dialer *Dialer
}
// AliveDialerSet assumes mapping between index and dialer MUST remain unchanged.
//
// It is thread-safe.
type AliveDialerSet struct {
2023-01-28 14:47:43 +07:00
log *logrus.Logger
dialerGroupName string
CheckTyp *NetworkType
tolerance time.Duration
2023-01-23 18:54:21 +07:00
aliveChangeCallback func(alive bool)
2023-01-23 18:54:21 +07:00
mu sync.Mutex
dialerToIndex map[*Dialer]int // *Dialer -> index of inorderedAliveDialerSet
dialerToLatency map[*Dialer]time.Duration
inorderedAliveDialerSet []*Dialer
selectionPolicy consts.DialerSelectionPolicy
minLatency minLatency
}
func NewAliveDialerSet(
log *logrus.Logger,
2023-01-28 14:47:43 +07:00
dialerGroupName string,
networkType *NetworkType,
tolerance time.Duration,
2023-01-23 18:54:21 +07:00
selectionPolicy consts.DialerSelectionPolicy,
dialers []*Dialer,
aliveChangeCallback func(alive bool),
2023-01-23 18:54:21 +07:00
setAlive bool,
) *AliveDialerSet {
a := &AliveDialerSet{
log: log,
2023-01-28 14:47:43 +07:00
dialerGroupName: dialerGroupName,
CheckTyp: networkType,
tolerance: tolerance,
aliveChangeCallback: aliveChangeCallback,
2023-01-23 18:54:21 +07:00
dialerToIndex: make(map[*Dialer]int),
dialerToLatency: make(map[*Dialer]time.Duration),
inorderedAliveDialerSet: make([]*Dialer, 0, len(dialers)),
selectionPolicy: selectionPolicy,
minLatency: minLatency{
// Initiate the latency with a very big value.
latency: time.Hour,
},
}
for _, d := range dialers {
a.dialerToIndex[d] = -1
}
for _, d := range dialers {
a.NotifyLatencyChange(d, setAlive)
2023-01-23 18:54:21 +07:00
}
return a
}
func (a *AliveDialerSet) GetRand() *Dialer {
a.mu.Lock()
defer a.mu.Unlock()
if len(a.inorderedAliveDialerSet) == 0 {
return nil
}
ind := fastrand.Intn(len(a.inorderedAliveDialerSet))
return a.inorderedAliveDialerSet[ind]
}
// GetMinLatency acquires correct selectionPolicy.
func (a *AliveDialerSet) GetMinLatency() (d *Dialer, latency time.Duration) {
return a.minLatency.dialer, a.minLatency.latency
2023-01-23 18:54:21 +07:00
}
// NotifyLatencyChange should be invoked when dialer every time latency and alive state changes.
func (a *AliveDialerSet) NotifyLatencyChange(dialer *Dialer, alive bool) {
2023-01-23 18:54:21 +07:00
a.mu.Lock()
defer a.mu.Unlock()
var (
latency time.Duration
hasLatency bool
2023-01-28 14:59:53 +07:00
minPolicy bool
2023-01-23 18:54:21 +07:00
)
switch a.selectionPolicy {
case consts.DialerSelectionPolicy_MinLastLatency:
latency, hasLatency = dialer.MustGetLatencies10(a.CheckTyp).LastLatency()
2023-01-28 14:59:53 +07:00
minPolicy = true
2023-01-23 18:54:21 +07:00
case consts.DialerSelectionPolicy_MinAverage10Latencies:
latency, hasLatency = dialer.MustGetLatencies10(a.CheckTyp).AvgLatency()
2023-01-28 14:59:53 +07:00
minPolicy = true
2023-01-23 18:54:21 +07:00
}
if alive {
index := a.dialerToIndex[dialer]
if index >= 0 {
// This dialer is already alive.
} else {
// Dialer: not alive -> alive.
2023-01-23 18:54:21 +07:00
a.dialerToIndex[dialer] = len(a.inorderedAliveDialerSet)
a.inorderedAliveDialerSet = append(a.inorderedAliveDialerSet, dialer)
}
} else {
index := a.dialerToIndex[dialer]
if index >= 0 {
// Dialer: alive -> not alive.
2023-01-23 18:54:21 +07:00
// Remove the dialer from inorderedAliveDialerSet.
if index >= len(a.inorderedAliveDialerSet) {
a.log.Panicf("index:%v >= len(a.inorderedAliveDialerSet):%v", index, len(a.inorderedAliveDialerSet))
}
a.dialerToIndex[dialer] = -1
if index < len(a.inorderedAliveDialerSet)-1 {
// Swap this element with the last element.
dialerToSwap := a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1]
if dialer == dialerToSwap {
a.log.Panicf("dialer[%p] == dialerToSwap[%p]", dialer, dialerToSwap)
}
a.dialerToIndex[dialerToSwap] = index
a.inorderedAliveDialerSet[index], a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1] =
a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1], a.inorderedAliveDialerSet[index]
}
// Pop the last element.
a.inorderedAliveDialerSet = a.inorderedAliveDialerSet[:len(a.inorderedAliveDialerSet)-1]
} else {
// This dialer is already not alive.
}
}
2023-01-28 14:59:53 +07:00
2023-01-23 18:54:21 +07:00
if hasLatency {
bakOldBestDialer := a.minLatency.dialer
2023-01-28 14:47:43 +07:00
// Calc minLatency.
2023-01-23 18:54:21 +07:00
a.dialerToLatency[dialer] = latency
if alive && latency <= a.minLatency.latency-a.tolerance {
2023-01-23 18:54:21 +07:00
a.minLatency.latency = latency
a.minLatency.dialer = dialer
} else if a.minLatency.dialer == dialer {
2023-02-11 23:14:00 +07:00
a.minLatency.latency = latency
if !alive || latency > a.minLatency.latency {
// Latency increases.
2023-02-11 23:14:00 +07:00
if !alive {
a.minLatency.dialer = nil
}
a.calcMinLatency()
// Now `a.minLatency.dialer` will be nil if there is no alive dialer.
}
2023-01-23 18:54:21 +07:00
}
currentAlive := a.minLatency.dialer != nil
// If best dialer changed.
if a.minLatency.dialer != bakOldBestDialer {
if currentAlive {
re := "re-"
var oldDialerName string
if bakOldBestDialer == nil {
// Not alive -> alive
defer a.aliveChangeCallback(true)
re = ""
oldDialerName = "<nil>"
} else {
oldDialerName = bakOldBestDialer.Name()
}
2023-02-04 14:02:44 +07:00
a.log.WithFields(logrus.Fields{
string(a.selectionPolicy): a.minLatency.latency,
"group": a.dialerGroupName,
"network": a.CheckTyp.String(),
"new dialer": a.minLatency.dialer.Name(),
"old dialer": oldDialerName,
}).Infof("Group %vselects dialer", re)
2023-01-30 14:50:55 +07:00
} else {
// Alive -> not alive
defer a.aliveChangeCallback(false)
2023-02-04 14:02:44 +07:00
a.log.WithFields(logrus.Fields{
"group": a.dialerGroupName,
"network": a.CheckTyp.String(),
2023-02-04 14:02:44 +07:00
}).Infof("Group has no dialer alive")
2023-01-29 10:19:58 +07:00
}
2023-01-28 14:47:43 +07:00
}
} else {
2023-01-28 14:59:53 +07:00
if alive && minPolicy && a.minLatency.dialer == nil {
2023-02-04 14:02:44 +07:00
// Use first dialer if no dialer has alive state (usually happen at the very beginning).
2023-01-28 14:47:43 +07:00
a.minLatency.dialer = dialer
2023-02-04 14:02:44 +07:00
a.log.WithFields(logrus.Fields{
"group": a.dialerGroupName,
"network": a.CheckTyp.String(),
"dialer": a.minLatency.dialer.Name(),
}).Infof("Group selects dialer")
2023-01-28 14:47:43 +07:00
}
2023-01-23 18:54:21 +07:00
}
}
func (a *AliveDialerSet) calcMinLatency() {
2023-02-11 23:14:00 +07:00
var minLatency = time.Hour
var minDialer *Dialer
2023-01-23 18:54:21 +07:00
for _, d := range a.inorderedAliveDialerSet {
latency, ok := a.dialerToLatency[d]
if !ok {
continue
}
2023-02-11 23:14:00 +07:00
if latency < minLatency {
minLatency = latency
minDialer = d
2023-01-23 18:54:21 +07:00
}
}
2023-02-11 23:14:00 +07:00
if a.minLatency.dialer == nil {
a.minLatency.latency = minLatency
a.minLatency.dialer = minDialer
} else if minDialer != nil && minLatency <= a.minLatency.latency-a.tolerance {
a.minLatency.latency = minLatency
a.minLatency.dialer = minDialer
}
2023-01-23 18:54:21 +07:00
}