mirror of
https://github.com/daeuniverse/dae.git
synced 2025-07-13 17:29:09 +07:00
feat: add check_tolerance to make node selection stable
This commit is contained in:
@ -26,6 +26,7 @@ type AliveDialerSet struct {
|
||||
dialerGroupName string
|
||||
l4proto consts.L4ProtoStr
|
||||
ipversion consts.IpVersionStr
|
||||
tolerance time.Duration
|
||||
|
||||
aliveChangeCallback func(alive bool)
|
||||
|
||||
@ -43,6 +44,7 @@ func NewAliveDialerSet(
|
||||
dialerGroupName string,
|
||||
l4proto consts.L4ProtoStr,
|
||||
ipversion consts.IpVersionStr,
|
||||
tolerance time.Duration,
|
||||
selectionPolicy consts.DialerSelectionPolicy,
|
||||
dialers []*Dialer,
|
||||
aliveChangeCallback func(alive bool),
|
||||
@ -53,6 +55,7 @@ func NewAliveDialerSet(
|
||||
dialerGroupName: dialerGroupName,
|
||||
l4proto: l4proto,
|
||||
ipversion: ipversion,
|
||||
tolerance: tolerance,
|
||||
aliveChangeCallback: aliveChangeCallback,
|
||||
dialerToIndex: make(map[*Dialer]int),
|
||||
dialerToLatency: make(map[*Dialer]time.Duration),
|
||||
@ -146,14 +149,19 @@ func (a *AliveDialerSet) NotifyLatencyChange(dialer *Dialer, alive bool) {
|
||||
bakOldBestDialer := a.minLatency.dialer
|
||||
// Calc minLatency.
|
||||
a.dialerToLatency[dialer] = latency
|
||||
if alive && latency < a.minLatency.latency {
|
||||
if alive && latency <= a.minLatency.latency-a.tolerance {
|
||||
a.minLatency.latency = latency
|
||||
a.minLatency.dialer = dialer
|
||||
} else if a.minLatency.dialer == dialer {
|
||||
a.minLatency.latency = time.Hour
|
||||
a.minLatency.dialer = nil
|
||||
a.calcMinLatency()
|
||||
// Now `a.minLatency.dialer` will be nil if there is no alive dialer.
|
||||
if latency > a.minLatency.latency {
|
||||
// Latency increases.
|
||||
a.minLatency.latency = time.Hour
|
||||
a.minLatency.dialer = nil
|
||||
a.calcMinLatency()
|
||||
// Now `a.minLatency.dialer` will be nil if there is no alive dialer.
|
||||
} else {
|
||||
a.minLatency.latency = latency
|
||||
}
|
||||
}
|
||||
currentAlive := a.minLatency.dialer != nil
|
||||
// If best dialer changed.
|
||||
@ -169,7 +177,8 @@ func (a *AliveDialerSet) NotifyLatencyChange(dialer *Dialer, alive bool) {
|
||||
string(a.selectionPolicy): a.minLatency.latency,
|
||||
"group": a.dialerGroupName,
|
||||
"network": string(a.l4proto) + string(a.ipversion),
|
||||
"dialer": a.minLatency.dialer.Name(),
|
||||
"new dialer": a.minLatency.dialer.Name(),
|
||||
"old dialer": bakOldBestDialer.Name(),
|
||||
}).Infof("Group %vselects dialer", re)
|
||||
} else {
|
||||
// Alive -> not alive
|
||||
@ -199,7 +208,7 @@ func (a *AliveDialerSet) calcMinLatency() {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if latency < a.minLatency.latency {
|
||||
if latency <= a.minLatency.latency-a.tolerance {
|
||||
a.minLatency.latency = latency
|
||||
a.minLatency.dialer = d
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ type GlobalOption struct {
|
||||
TcpCheckOptionRaw TcpCheckOptionRaw // Lazy parse
|
||||
UdpCheckOptionRaw UdpCheckOptionRaw // Lazy parse
|
||||
CheckInterval time.Duration
|
||||
CheckTolerance time.Duration
|
||||
}
|
||||
|
||||
type InstanceOption struct {
|
||||
|
@ -39,10 +39,10 @@ type DialerGroup struct {
|
||||
func NewDialerGroup(option *dialer.GlobalOption, name string, dialers []*dialer.Dialer, p DialerSelectionPolicy, aliveChangeCallback func(alive bool, l4proto uint8, ipversion uint8)) *DialerGroup {
|
||||
log := option.Log
|
||||
var registeredAliveDialerSet bool
|
||||
aliveTcp4DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_TCP, consts.IpVersionStr_4, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_TCP, 4) }, true)
|
||||
aliveTcp6DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_TCP, consts.IpVersionStr_6, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_TCP, 6) }, true)
|
||||
aliveUdp4DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_UDP, consts.IpVersionStr_4, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_UDP, 4) }, true)
|
||||
aliveUdp6DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_UDP, consts.IpVersionStr_6, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_UDP, 6) }, true)
|
||||
aliveTcp4DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_TCP, consts.IpVersionStr_4, option.CheckTolerance, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_TCP, 4) }, true)
|
||||
aliveTcp6DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_TCP, consts.IpVersionStr_6, option.CheckTolerance, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_TCP, 6) }, true)
|
||||
aliveUdp4DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_UDP, consts.IpVersionStr_4, option.CheckTolerance, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_UDP, 4) }, true)
|
||||
aliveUdp6DialerSet := dialer.NewAliveDialerSet(log, name, consts.L4ProtoStr_UDP, consts.IpVersionStr_6, option.CheckTolerance, p.Policy, dialers, func(alive bool) { aliveChangeCallback(alive, unix.IPPROTO_UDP, 6) }, true)
|
||||
|
||||
switch p.Policy {
|
||||
case consts.DialerSelectionPolicy_Random,
|
||||
|
@ -14,14 +14,15 @@ import (
|
||||
)
|
||||
|
||||
type Global struct {
|
||||
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
|
||||
LogLevel string `mapstructure:"log_level" default:"info"`
|
||||
TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://cp.cloudflare.com"`
|
||||
UdpCheckDns string `mapstructure:"udp_check_dns" default:"cloudflare-dns.com:53"`
|
||||
CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"`
|
||||
DnsUpstream common.UrlOrEmpty `mapstructure:"dns_upstream" require:""`
|
||||
LanInterface []string `mapstructure:"lan_interface"`
|
||||
WanInterface []string `mapstructure:"wan_interface"`
|
||||
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
|
||||
LogLevel string `mapstructure:"log_level" default:"info"`
|
||||
TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://cp.cloudflare.com"`
|
||||
UdpCheckDns string `mapstructure:"udp_check_dns" default:"cloudflare-dns.com:53"`
|
||||
CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"`
|
||||
CheckTolerance time.Duration `mapstructure:"check_tolerance" default:"0"`
|
||||
DnsUpstream common.UrlOrEmpty `mapstructure:"dns_upstream" require:""`
|
||||
LanInterface []string `mapstructure:"lan_interface"`
|
||||
WanInterface []string `mapstructure:"wan_interface"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
|
@ -196,6 +196,7 @@ func NewControlPlane(
|
||||
TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl},
|
||||
UdpCheckOptionRaw: dialer.UdpCheckOptionRaw{Raw: global.UdpCheckDns},
|
||||
CheckInterval: global.CheckInterval,
|
||||
CheckTolerance: global.CheckTolerance,
|
||||
}
|
||||
outbounds := []*outbound.DialerGroup{
|
||||
outbound.NewDialerGroup(option, consts.OutboundDirect.String(),
|
||||
|
@ -10,6 +10,8 @@ global {
|
||||
tcp_check_url: 'http://cp.cloudflare.com'
|
||||
udp_check_dns: 'dns.google:53'
|
||||
check_interval: 30s
|
||||
# Group will switch node only when new_latency <= old_latency - tolerance
|
||||
check_tolerance: 50ms
|
||||
|
||||
# Value can be scheme://host:port or empty string ''.
|
||||
# The scheme can be tcp/udp/tcp+udp. Empty string '' indicates as-is.
|
||||
|
Reference in New Issue
Block a user