1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-03 06:30:42 +00:00
v2fly/app/router/router_test.go
Jebbs 2c5a714903
v5: Health Check & LeastLoad Strategy (#589)
* generate .pb.go

* health checker conf

* check logic

* implement ping

* fix check interval

* improve check results

* health check on add outbounds

* fix tests

* fix ping handler

* fix min rtt < 0

* random alive

* fix check all on add outbounds

* least load strategy

* conf codes optimize

* improve least load strategy

* improve health check on AddOutboundHandler

* cleanup results with scheduler
code optimize

* health ping timeout default 5s

* remove config of health ping round
round 1 seems to be good enough

* fix TestSimpleBalancer

* add TestLeastLoadBalancer

* add todos

* lint and test fix

* balancer fallback

* api health stats command

* add hc cmd to perform health checks
* rename 'health stats' cmd to hci
* many code optimizations

* fix typo

* select none if no match for baselines only config
> prev 'select 1' behavior can achieved by baselines+expected=1

* add LeastLoadStrategy tests

* don't select alive on no match, go to fallback

* api hci refactor
* more detailed info
* ready for future new strategies

* apply lint style

* refactor: strategies don't need ref of balancer

* change check interval unit to seconds
> to reduce influence caused by what is described by new added FIXME

* fix test

* RouterService->RoutingService

* Revert "generate .pb.go"

This reverts commit 0e6fa1be889470d0ad9692f7279da45c030e1919.

* make checks distributed
> but `api hc` is the exception

* BalancingStrategy interface optimize

* fix random selects unchecked

* upgrade cmd hci to bi & rename hc to bc
* bi shows all balancers, while hci shows only heath-check-enabled ones
* shows more info

* fix test

* api bi sort output

* update according to review

* remove checks on add outbound

* refactor: move health checker inside to strategy
* enables rounds setting for health ping
* restore the random behavior, no ping, no pick alive

> if future strategy based on HealthPing, just embed it like what LeastLoad does

* apply lint style

* code optimize

* fix typo

* update desc of bc bi

* ping with head
code optimize

* force rouds to 1 if checks not distributed

* leatload: select by standard deviations

* health ping refactor
* continuously applying results
* config is easier to understand
* checker interfaces simplifying

* add maxRTT config to filter away high delay nodes

* apply lint

* cost for leastload

* api bo to override balancer selecting

* fix health ping statistics & fix test

* check connectivity if ping fail

* add tolerance setting & more detailed bi output

* fix connectivity check

* optimize bi output

* should not put results when network is down

* fixes @_@

* mux optimize

* remove pause option of selecting overriding
> it causes data racing

* update bo desc

* fix potential racing

* simplify locking
* switch sync.Mutex to avoid potential racing
* add more tests
* code optimize

* code optimize

* fix connectivity check when url not set
2021-01-30 08:31:11 +08:00

246 lines
6.2 KiB
Go

package router_test
import (
"context"
"testing"
"github.com/golang/mock/gomock"
. "v2ray.com/core/app/router"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
serial "v2ray.com/core/common/serial"
"v2ray.com/core/common/session"
"v2ray.com/core/features/outbound"
routing_session "v2ray.com/core/features/routing/session"
"v2ray.com/core/testing/mocks"
)
type mockOutboundManager struct {
outbound.Manager
outbound.HandlerSelector
}
func TestSimpleRouter(t *testing.T) {
config := &Config{
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Networks: []net.Network{net.Network_TCP},
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
mockOhm := mocks.NewOutboundManager(mockCtl)
mockHs := mocks.NewOutboundHandlerSelector(mockCtl)
r := new(Router)
common.Must(r.Init(config, mockDNS, &mockOutboundManager{
Manager: mockOhm,
HandlerSelector: mockHs,
}, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}
func TestSimpleBalancer(t *testing.T) {
config := &Config{
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_BalancingTag{
BalancingTag: "balance",
},
Networks: []net.Network{net.Network_TCP},
},
},
BalancingRule: []*BalancingRule{
{
Tag: "balance",
OutboundSelector: []string{"test-"},
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
mockOhm := mocks.NewOutboundManager(mockCtl)
mockHs := mocks.NewOutboundHandlerSelector(mockCtl)
mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test"})
r := new(Router)
common.Must(r.Init(config, mockDNS, &mockOutboundManager{
Manager: mockOhm,
HandlerSelector: mockHs,
}, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}
func TestLeastLoadBalancer(t *testing.T) {
config := &Config{
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_BalancingTag{
BalancingTag: "balance",
},
Networks: []net.Network{net.Network_TCP},
},
},
BalancingRule: []*BalancingRule{
{
Tag: "balance",
OutboundSelector: []string{"test-"},
Strategy: BalancingRule_LeastLoad,
StrategySettings: serial.ToTypedMessage(&StrategyLeastLoadConfig{
HealthCheck: nil,
Baselines: nil,
Expected: 1,
}),
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
mockOhm := mocks.NewOutboundManager(mockCtl)
mockHs := mocks.NewOutboundHandlerSelector(mockCtl)
mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test1"})
r := new(Router)
common.Must(r.Init(config, mockDNS, &mockOutboundManager{
Manager: mockOhm,
HandlerSelector: mockHs,
}, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test1" {
t.Error("expect tag 'test1', bug actually ", tag)
}
}
func TestIPOnDemand(t *testing.T) {
config := &Config{
DomainStrategy: Config_IpOnDemand,
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
Prefix: 16,
},
},
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
mockDNS.EXPECT().LookupIP(gomock.Eq("v2ray.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
r := new(Router)
common.Must(r.Init(config, mockDNS, nil, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}
func TestIPIfNonMatchDomain(t *testing.T) {
config := &Config{
DomainStrategy: Config_IpIfNonMatch,
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
Prefix: 16,
},
},
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
mockDNS.EXPECT().LookupIP(gomock.Eq("v2ray.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
r := new(Router)
common.Must(r.Init(config, mockDNS, nil, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}
func TestIPIfNonMatchIP(t *testing.T) {
config := &Config{
DomainStrategy: Config_IpIfNonMatch,
Rule: []*RoutingRule{
{
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Cidr: []*CIDR{
{
Ip: []byte{127, 0, 0, 0},
Prefix: 8,
},
},
},
},
}
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl)
r := new(Router)
common.Must(r.Init(config, mockDNS, nil, nil))
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 80)})
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
common.Must(err)
if tag := route.GetOutboundTag(); tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}