From 02434ca34b59767667f2e7ea5063ef097e432f1f Mon Sep 17 00:00:00 2001 From: Shelikhoo Date: Sun, 15 Feb 2026 11:49:02 +0000 Subject: [PATCH] Add sticky choice option for leastping --- app/router/config.pb.go | 13 +++++++++++-- app/router/config.proto | 2 ++ app/router/strategy_leastping.go | 12 ++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/router/config.pb.go b/app/router/config.pb.go index 97d1618b9..d4d4594fc 100644 --- a/app/router/config.pb.go +++ b/app/router/config.pb.go @@ -507,6 +507,7 @@ func (x *StrategyRandomConfig) GetAliveOnly() bool { type StrategyLeastPingConfig struct { state protoimpl.MessageState `protogen:"open.v1"` ObserverTag string `protobuf:"bytes,7,opt,name=observer_tag,json=observerTag,proto3" json:"observer_tag,omitempty"` + StickyChoice bool `protobuf:"varint,8,opt,name=sticky_choice,json=stickyChoice,proto3" json:"sticky_choice,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -548,6 +549,13 @@ func (x *StrategyLeastPingConfig) GetObserverTag() string { return "" } +func (x *StrategyLeastPingConfig) GetStickyChoice() bool { + if x != nil { + return x.StickyChoice + } + return false +} + type StrategyLeastLoadConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // weight settings @@ -996,9 +1004,10 @@ const file_app_router_config_proto_rawDesc = "" + "\fobserver_tag\x18\a \x01(\tR\vobserverTag\x12\x1d\n" + "\n" + "alive_only\x18\b \x01(\bR\taliveOnly:\x16\x82\xb5\x18\x12\n" + - "\bbalancer\x12\x06random\"W\n" + + "\bbalancer\x12\x06random\"|\n" + "\x17StrategyLeastPingConfig\x12!\n" + - "\fobserver_tag\x18\a \x01(\tR\vobserverTag:\x19\x82\xb5\x18\x15\n" + + "\fobserver_tag\x18\a \x01(\tR\vobserverTag\x12#\n" + + "\rsticky_choice\x18\b \x01(\bR\fstickyChoice:\x19\x82\xb5\x18\x15\n" + "\bbalancer\x12\tleastping\"\x84\x02\n" + "\x17StrategyLeastLoadConfig\x12;\n" + "\x05costs\x18\x02 \x03(\v2%.v2ray.core.app.router.StrategyWeightR\x05costs\x12\x1c\n" + diff --git a/app/router/config.proto b/app/router/config.proto index 1ca01ed02..3538b7345 100644 --- a/app/router/config.proto +++ b/app/router/config.proto @@ -97,6 +97,8 @@ message StrategyLeastPingConfig { option (v2ray.core.common.protoext.message_opt).short_name = "leastping"; string observer_tag = 7; + + bool sticky_choice = 8; } message StrategyLeastLoadConfig { diff --git a/app/router/strategy_leastping.go b/app/router/strategy_leastping.go index ea9337052..59658bc62 100644 --- a/app/router/strategy_leastping.go +++ b/app/router/strategy_leastping.go @@ -5,6 +5,7 @@ package router import ( "context" + "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" @@ -18,6 +19,9 @@ type LeastPingStrategy struct { observatory extension.Observatory config *StrategyLeastPingConfig + + lock sync.Mutex + lastChoice string } func (l *LeastPingStrategy) GetPrincipleTarget(strings []string) []string { @@ -61,6 +65,14 @@ func (l *LeastPingStrategy) PickOutbound(strings []string) string { leastPing = v.Delay } } + { + l.lock.Lock() + if selectedOutboundName == "" && l.config.StickyChoice { + selectedOutboundName = l.lastChoice + } + l.lastChoice = selectedOutboundName + l.lock.Unlock() + } return selectedOutboundName }