github.com/cloudwego/hertz@v0.9.3/pkg/app/client/loadbalance/lbcache_test.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package loadbalance 18 19 import ( 20 "context" 21 "fmt" 22 "sync/atomic" 23 "testing" 24 "time" 25 26 "github.com/cloudwego/hertz/pkg/app/client/discovery" 27 "github.com/cloudwego/hertz/pkg/common/test/assert" 28 "github.com/cloudwego/hertz/pkg/protocol" 29 ) 30 31 func TestBuilder(t *testing.T) { 32 ins := discovery.NewInstance("tcp", "127.0.0.1:8888", 10, map[string]string{"a": "b"}) 33 r := &discovery.SynthesizedResolver{ 34 ResolveFunc: func(ctx context.Context, key string) (discovery.Result, error) { 35 return discovery.Result{CacheKey: key, Instances: []discovery.Instance{ins}}, nil 36 }, 37 TargetFunc: func(ctx context.Context, target *discovery.TargetInfo) string { 38 return "mockRoute" 39 }, 40 NameFunc: func() string { return t.Name() }, 41 } 42 lb := mockLoadbalancer{ 43 rebalanceFunc: nil, 44 deleteFunc: nil, 45 pickFunc: func(res discovery.Result) discovery.Instance { 46 assert.Assert(t, res.CacheKey == t.Name()+":mockRoute", res.CacheKey) 47 assert.Assert(t, len(res.Instances) == 1) 48 assert.Assert(t, len(res.Instances) == 1) 49 assert.Assert(t, res.Instances[0].Address().String() == "127.0.0.1:8888") 50 return res.Instances[0] 51 }, 52 nameFunc: func() string { return "Synthesized" }, 53 } 54 NewBalancerFactory(Config{ 55 Balancer: lb, 56 LbOpts: DefaultLbOpts, 57 Resolver: r, 58 }) 59 b, ok := balancerFactories.Load(cacheKey(t.Name(), "Synthesized", DefaultLbOpts)) 60 assert.Assert(t, ok) 61 assert.Assert(t, b != nil) 62 req := &protocol.Request{} 63 req.SetHost("hertz.api.test") 64 ins1, err := b.(*BalancerFactory).GetInstance(context.TODO(), req) 65 assert.Assert(t, err == nil) 66 assert.Assert(t, ins1.Address().String() == "127.0.0.1:8888") 67 assert.Assert(t, ins1.Weight() == 10) 68 value, exists := ins1.Tag("a") 69 assert.Assert(t, value == "b") 70 assert.Assert(t, exists == true) 71 } 72 73 func TestBalancerCache(t *testing.T) { 74 count := 10 75 inss := make([]discovery.Instance, 0, count) 76 for i := 0; i < count; i++ { 77 inss = append(inss, discovery.NewInstance("tcp", fmt.Sprint(i), 10, nil)) 78 } 79 r := &discovery.SynthesizedResolver{ 80 TargetFunc: func(ctx context.Context, target *discovery.TargetInfo) string { 81 return target.Host 82 }, 83 ResolveFunc: func(ctx context.Context, key string) (discovery.Result, error) { 84 return discovery.Result{CacheKey: "svc", Instances: inss}, nil 85 }, 86 NameFunc: func() string { return t.Name() }, 87 } 88 lb := NewWeightedBalancer() 89 for i := 0; i < count; i++ { 90 blf := NewBalancerFactory(Config{ 91 Balancer: lb, 92 LbOpts: Options{}, 93 Resolver: r, 94 }) 95 req := &protocol.Request{} 96 req.SetHost("svc") 97 for a := 0; a < count; a++ { 98 addr, err := blf.GetInstance(context.TODO(), req) 99 assert.Assert(t, err == nil, err) 100 t.Logf("count: %d addr: %s\n", i, addr.Address().String()) 101 } 102 } 103 } 104 105 func TestBalancerRefresh(t *testing.T) { 106 var ins atomic.Value 107 ins.Store(discovery.NewInstance("tcp", "127.0.0.1:8888", 10, nil)) 108 r := &discovery.SynthesizedResolver{ 109 TargetFunc: func(ctx context.Context, target *discovery.TargetInfo) string { 110 return target.Host 111 }, 112 ResolveFunc: func(ctx context.Context, key string) (discovery.Result, error) { 113 return discovery.Result{CacheKey: "svc1", Instances: []discovery.Instance{ins.Load().(discovery.Instance)}}, nil 114 }, 115 NameFunc: func() string { return t.Name() }, 116 } 117 blf := NewBalancerFactory(Config{ 118 Balancer: NewWeightedBalancer(), 119 LbOpts: DefaultLbOpts, 120 Resolver: r, 121 }) 122 req := &protocol.Request{} 123 req.SetHost("svc1") 124 addr, err := blf.GetInstance(context.Background(), req) 125 assert.Assert(t, err == nil, err) 126 assert.Assert(t, addr.Address().String() == "127.0.0.1:8888") 127 ins.Store(discovery.NewInstance("tcp", "127.0.0.1:8889", 10, nil)) 128 addr, err = blf.GetInstance(context.Background(), req) 129 assert.Assert(t, err == nil, err) 130 assert.Assert(t, addr.Address().String() == "127.0.0.1:8888") 131 time.Sleep(6 * time.Second) 132 addr, err = blf.GetInstance(context.Background(), req) 133 assert.Assert(t, err == nil, err) 134 assert.Assert(t, addr.Address().String() == "127.0.0.1:8889") 135 } 136 137 func TestCacheKey(t *testing.T) { 138 uniqueKey := cacheKey("hello", "world", Options{RefreshInterval: 15 * time.Second, ExpireInterval: 5 * time.Minute}) 139 assert.Assert(t, uniqueKey == "hello|world|{15s 5m0s}") 140 } 141 142 type mockLoadbalancer struct { 143 rebalanceFunc func(ch discovery.Result) 144 deleteFunc func(key string) 145 pickFunc func(discovery.Result) discovery.Instance 146 nameFunc func() string 147 } 148 149 // Rebalance implements the Loadbalancer interface. 150 func (m mockLoadbalancer) Rebalance(ch discovery.Result) { 151 if m.rebalanceFunc != nil { 152 m.rebalanceFunc(ch) 153 } 154 } 155 156 // Delete implements the Loadbalancer interface. 157 func (m mockLoadbalancer) Delete(ch string) { 158 if m.deleteFunc != nil { 159 m.deleteFunc(ch) 160 } 161 } 162 163 // Name implements the Loadbalancer interface. 164 func (m mockLoadbalancer) Name() string { 165 if m.nameFunc != nil { 166 return m.nameFunc() 167 } 168 return "" 169 } 170 171 // Pick implements the Loadbalancer interface. 172 func (m mockLoadbalancer) Pick(d discovery.Result) discovery.Instance { 173 if m.pickFunc != nil { 174 return m.pickFunc(d) 175 } 176 return nil 177 }