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