2023-01-23 18:54:21 +07:00
|
|
|
/*
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2023-02-18 17:27:28 +07:00
|
|
|
* Copyright (c) 2022-2023, v2rayA Organization <team@v2raya.org>
|
2023-01-23 18:54:21 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
package dialer
|
|
|
|
|
|
|
|
import (
|
2023-02-13 14:27:36 +07:00
|
|
|
"fmt"
|
2023-01-23 18:54:21 +07:00
|
|
|
"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-02-13 14:27:36 +07:00
|
|
|
"strings"
|
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
|
2023-02-12 14:39:00 +07:00
|
|
|
CheckTyp *NetworkType
|
2023-02-09 20:16:51 +07:00
|
|
|
tolerance time.Duration
|
2023-01-23 18:54:21 +07:00
|
|
|
|
2023-02-08 19:54:28 +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,
|
2023-02-12 14:39:00 +07:00
|
|
|
networkType *NetworkType,
|
2023-02-09 20:16:51 +07:00
|
|
|
tolerance time.Duration,
|
2023-01-23 18:54:21 +07:00
|
|
|
selectionPolicy consts.DialerSelectionPolicy,
|
|
|
|
dialers []*Dialer,
|
2023-02-08 19:54:28 +07:00
|
|
|
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,
|
2023-02-12 14:39:00 +07:00
|
|
|
CheckTyp: networkType,
|
2023-02-09 20:16:51 +07:00
|
|
|
tolerance: tolerance,
|
2023-02-08 19:54:28 +07:00
|
|
|
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 {
|
2023-02-08 22:10:09 +07:00
|
|
|
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.
|
2023-02-09 10:40:34 +07:00
|
|
|
func (a *AliveDialerSet) GetMinLatency() (d *Dialer, latency time.Duration) {
|
|
|
|
return a.minLatency.dialer, a.minLatency.latency
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
|
2023-02-13 14:27:36 +07:00
|
|
|
func (a *AliveDialerSet) printLatencies() {
|
2023-02-19 21:16:59 +07:00
|
|
|
if !a.log.IsLevelEnabled(logrus.TraceLevel) {
|
|
|
|
return
|
|
|
|
}
|
2023-02-13 14:27:36 +07:00
|
|
|
var builder strings.Builder
|
|
|
|
builder.WriteString(fmt.Sprintf("%v (%v):\n", a.dialerGroupName, a.CheckTyp.String()))
|
|
|
|
for _, d := range a.inorderedAliveDialerSet {
|
|
|
|
latency, ok := a.dialerToLatency[d]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
builder.WriteString(fmt.Sprintf("%v: %v\n", d.Name(), latency.String()))
|
|
|
|
}
|
|
|
|
a.log.Traceln(builder.String())
|
|
|
|
}
|
|
|
|
|
2023-02-08 22:10:09 +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:
|
2023-02-19 00:49:36 +07:00
|
|
|
latency, hasLatency = dialer.mustGetCollection(a.CheckTyp).Latencies10.LastLatency()
|
2023-01-28 14:59:53 +07:00
|
|
|
minPolicy = true
|
2023-01-23 18:54:21 +07:00
|
|
|
case consts.DialerSelectionPolicy_MinAverage10Latencies:
|
2023-02-19 00:49:36 +07:00
|
|
|
latency, hasLatency = dialer.mustGetCollection(a.CheckTyp).Latencies10.AvgLatency()
|
|
|
|
minPolicy = true
|
|
|
|
case consts.DialerSelectionPolicy_MinMovingAverageLatencies:
|
|
|
|
latency = dialer.mustGetCollection(a.CheckTyp).MovingAverage
|
|
|
|
hasLatency = latency > 0
|
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 {
|
2023-02-08 21:43:28 +07:00
|
|
|
// Dialer: not alive -> alive.
|
2023-02-17 23:49:35 +07:00
|
|
|
a.log.WithFields(logrus.Fields{
|
|
|
|
"dialer": dialer.Name(),
|
|
|
|
"network": a.CheckTyp.StringWithoutDns(),
|
|
|
|
}).Infoln("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 {
|
2023-02-08 21:43:28 +07:00
|
|
|
// Dialer: alive -> not alive.
|
2023-02-17 23:49:35 +07:00
|
|
|
a.log.WithFields(logrus.Fields{
|
|
|
|
"dialer": dialer.Name(),
|
|
|
|
"network": a.CheckTyp.StringWithoutDns(),
|
|
|
|
}).Infoln("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 {
|
2023-02-08 22:10:09 +07:00
|
|
|
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
|
2023-02-13 14:27:36 +07:00
|
|
|
if alive &&
|
|
|
|
latency <= a.minLatency.latency && // To avoid arithmetic overflow.
|
|
|
|
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
|
2023-02-11 21:24:14 +07:00
|
|
|
if !alive || latency > a.minLatency.latency {
|
2023-02-09 20:16:51 +07:00
|
|
|
// Latency increases.
|
2023-02-11 23:14:00 +07:00
|
|
|
if !alive {
|
|
|
|
a.minLatency.dialer = nil
|
|
|
|
}
|
2023-02-09 20:16:51 +07:00
|
|
|
a.calcMinLatency()
|
|
|
|
// Now `a.minLatency.dialer` will be nil if there is no alive dialer.
|
|
|
|
}
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
2023-02-08 22:10:09 +07:00
|
|
|
currentAlive := a.minLatency.dialer != nil
|
|
|
|
// If best dialer changed.
|
|
|
|
if a.minLatency.dialer != bakOldBestDialer {
|
|
|
|
if currentAlive {
|
2023-02-08 21:43:28 +07:00
|
|
|
re := "re-"
|
2023-02-11 21:26:28 +07:00
|
|
|
var oldDialerName string
|
2023-02-08 22:10:09 +07:00
|
|
|
if bakOldBestDialer == nil {
|
|
|
|
// Not alive -> alive
|
2023-02-08 21:43:28 +07:00
|
|
|
defer a.aliveChangeCallback(true)
|
|
|
|
re = ""
|
2023-02-11 21:26:28 +07:00
|
|
|
oldDialerName = "<nil>"
|
|
|
|
} else {
|
|
|
|
oldDialerName = bakOldBestDialer.Name()
|
2023-02-08 21:43:28 +07:00
|
|
|
}
|
2023-02-04 14:02:44 +07:00
|
|
|
a.log.WithFields(logrus.Fields{
|
|
|
|
string(a.selectionPolicy): a.minLatency.latency,
|
|
|
|
"group": a.dialerGroupName,
|
2023-02-12 14:39:00 +07:00
|
|
|
"network": a.CheckTyp.String(),
|
2023-02-09 20:16:51 +07:00
|
|
|
"new dialer": a.minLatency.dialer.Name(),
|
2023-02-11 21:26:28 +07:00
|
|
|
"old dialer": oldDialerName,
|
2023-02-08 21:43:28 +07:00
|
|
|
}).Infof("Group %vselects dialer", re)
|
2023-01-30 14:50:55 +07:00
|
|
|
} else {
|
2023-02-08 22:10:09 +07:00
|
|
|
// Alive -> not alive
|
2023-02-08 21:43:28 +07:00
|
|
|
defer a.aliveChangeCallback(false)
|
2023-02-04 14:02:44 +07:00
|
|
|
a.log.WithFields(logrus.Fields{
|
2023-02-08 19:15:24 +07:00
|
|
|
"group": a.dialerGroupName,
|
2023-02-12 14:39:00 +07:00
|
|
|
"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-02-13 14:27:36 +07:00
|
|
|
if a.log.IsLevelEnabled(logrus.TraceLevel) {
|
|
|
|
a.printLatencies()
|
|
|
|
}
|
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{
|
2023-02-08 19:15:24 +07:00
|
|
|
"group": a.dialerGroupName,
|
2023-02-12 14:39:00 +07:00
|
|
|
"network": a.CheckTyp.String(),
|
2023-02-08 19:15:24 +07:00
|
|
|
"dialer": a.minLatency.dialer.Name(),
|
2023-02-04 14:28:48 +07:00
|
|
|
}).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 {
|
2023-02-09 19:54:06 +07:00
|
|
|
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
|
2023-02-13 14:27:36 +07:00
|
|
|
} else if minDialer != nil &&
|
|
|
|
minLatency <= a.minLatency.latency && // To avoid arithmetic overflow.
|
|
|
|
minLatency <= a.minLatency.latency-a.tolerance {
|
2023-02-11 23:14:00 +07:00
|
|
|
a.minLatency.latency = minLatency
|
|
|
|
a.minLatency.dialer = minDialer
|
|
|
|
}
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|