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  }