From e7eeb9ca16a3e43baee38d29ff5c07d590b9aebd Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 10:25:29 +0800 Subject: [PATCH 1/6] add test --- client/selector_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/client/selector_test.go b/client/selector_test.go index 0e0b7634..02b789cc 100644 --- a/client/selector_test.go +++ b/client/selector_test.go @@ -1,6 +1,7 @@ package client import ( + "context" "testing" ) @@ -36,3 +37,22 @@ func Test_consistentHashSelector_UpdateServer(t *testing.T) { t.Errorf("UpdateServer: expected %d server but got %d", len(servers), len(s.h.All())) } } + +func TestWeightedRoundRobinSelector_Select(t *testing.T) { + // a b a c a b a a b a c a b a + sers := []string{"ServerA", "ServerB", "ServerA", "ServerC", "ServerA", "ServerB", "ServerA", + "ServerA", "ServerB", "ServerA", "ServerC", "ServerA", "ServerB", "ServerA"} + servers := make(map[string]string) + servers["ServerA"] = "weight=4" + servers["ServerB"] = "weight=2" + servers["ServerC"] = "weight=1" + ctx := context.Background() + weightSelector := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector) + + for i := 0; i < 14; i++ { + s := weightSelector.Select(ctx, "", "", nil) + if s != sers[i] { + t.Errorf("expected %s but got %s", sers[i], s) + } + } +} From 3a5aa78f15b8c6c00f09307c6e51529c24943acb Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 10:38:23 +0800 Subject: [PATCH 2/6] add benchmark --- client/selector_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/selector_test.go b/client/selector_test.go index 02b789cc..01d4d712 100644 --- a/client/selector_test.go +++ b/client/selector_test.go @@ -56,3 +56,16 @@ func TestWeightedRoundRobinSelector_Select(t *testing.T) { } } } + +func BenchmarkWeightedRoundRobinSelector_Select(b *testing.B) { + servers := make(map[string]string) + servers["ServerA"] = "weight=4" + servers["ServerB"] = "weight=2" + servers["ServerC"] = "weight=1" + ctx := context.Background() + weightSelector := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector) + + for i := 0; i < b.N; i++ { + weightSelector.Select(ctx, "", "", nil) + } +} From 6cd6940d268dc877e0d373feeca8113498a68665 Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 10:38:48 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=80=A7=E8=83=BD=E6=8F=90=E5=8D=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/selector.go | 57 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/client/selector.go b/client/selector.go index 1688b934..10dfd52c 100644 --- a/client/selector.go +++ b/client/selector.go @@ -1,6 +1,7 @@ package client import ( + "container/ring" "context" "math" "math/rand" @@ -111,12 +112,16 @@ func (s *roundRobinSelector) UpdateServer(servers map[string]string) { // weightedRoundRobinSelector selects servers with weighted. type weightedRoundRobinSelector struct { - servers []*Weighted + servers []*Weighted + totalWeight int + rr *ring.Ring } func newWeightedRoundRobinSelector(servers map[string]string) Selector { ss := createWeighted(servers) - return &weightedRoundRobinSelector{servers: ss} + s := &weightedRoundRobinSelector{servers: ss} + s.buildRing() + return s } func (s *weightedRoundRobinSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { @@ -124,29 +129,61 @@ func (s *weightedRoundRobinSelector) Select(ctx context.Context, servicePath, se if len(ss) == 0 { return "" } - w := nextWeighted(ss) - if w == nil { - return "" - } - return w.Server + val := s.rr.Value + s.rr = s.rr.Next() + return val.(*Weighted).Server + } func (s *weightedRoundRobinSelector) UpdateServer(servers map[string]string) { ss := createWeighted(servers) s.servers = ss } - +func (s *weightedRoundRobinSelector) buildRing() { + s.totalWeight = 0 + for _, w := range s.servers { + s.totalWeight += w.Weight + } + s.rr = ring.New(s.totalWeight) + for i := 0; i < s.totalWeight; i++ { + n := s.next() + s.rr.Value = n + s.rr = s.rr.Next() + } +} +func (s *weightedRoundRobinSelector) next() *Weighted { + if len(s.servers) == 0 { + return nil + } + n := len(s.servers) + if n == 0 { + return nil + } + if n == 1 { + return s.servers[0] + } + flag := 0 + m := 0 + for i := 0; i < n; i++ { + s.servers[i].CurrentWeight += s.servers[i].Weight + if s.servers[i].CurrentWeight > m { + m = s.servers[i].CurrentWeight + flag = i + } + } + s.servers[flag].CurrentWeight -= s.totalWeight + return s.servers[flag] +} func createWeighted(servers map[string]string) []*Weighted { ss := make([]*Weighted, 0, len(servers)) for k, metadata := range servers { - w := &Weighted{Server: k, Weight: 1, EffectiveWeight: 1} + w := &Weighted{Server: k, Weight: 1} if v, err := url.ParseQuery(metadata); err == nil { ww := v.Get("weight") if ww != "" { if weight, err := strconv.Atoi(ww); err == nil { w.Weight = weight - w.EffectiveWeight = weight } } } From 90619a33534b74ea60a4f08ddbba66f28c552e3a Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 15:58:11 +0800 Subject: [PATCH 4/6] fix rebuild --- client/selector.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/selector.go b/client/selector.go index 10dfd52c..7fd9f1fc 100644 --- a/client/selector.go +++ b/client/selector.go @@ -137,6 +137,7 @@ func (s *weightedRoundRobinSelector) Select(ctx context.Context, servicePath, se func (s *weightedRoundRobinSelector) UpdateServer(servers map[string]string) { ss := createWeighted(servers) + s.buildRing() s.servers = ss } func (s *weightedRoundRobinSelector) buildRing() { From 07477694098ae9e1ff6e1197edecc9229f4bf10b Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 16:13:48 +0800 Subject: [PATCH 5/6] rename variable --- client/selector.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/selector.go b/client/selector.go index 7fd9f1fc..40cc8f53 100644 --- a/client/selector.go +++ b/client/selector.go @@ -221,13 +221,13 @@ func (s geoSelector) Select(ctx context.Context, servicePath, serviceMethod stri } var server []string - min := math.MaxFloat64 + minNum := math.MaxFloat64 for _, gs := range s.servers { d := getDistanceFrom(s.Latitude, s.Longitude, gs.Latitude, gs.Longitude) - if d < min { + if d < minNum { server = []string{gs.Server} - min = d - } else if d == min { + minNum = d + } else if d == minNum { server = append(server, gs.Server) } } From 98b076ce4c8cbeaeb09ef8a31059473a598595a0 Mon Sep 17 00:00:00 2001 From: yanshushuang Date: Wed, 10 Jan 2024 16:16:12 +0800 Subject: [PATCH 6/6] fix both have pointer and value receivers --- client/selector.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/selector.go b/client/selector.go index 40cc8f53..0cefb8c3 100644 --- a/client/selector.go +++ b/client/selector.go @@ -56,7 +56,7 @@ func newRandomSelector(servers map[string]string) Selector { return &randomSelector{servers: ss} } -func (s randomSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { +func (s *randomSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { ss := s.servers if len(ss) == 0 { return "" @@ -215,7 +215,7 @@ func newGeoSelector(servers map[string]string, latitude, longitude float64) Sele return &geoSelector{servers: ss, Latitude: latitude, Longitude: longitude, r: r} } -func (s geoSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { +func (s *geoSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { if len(s.servers) == 0 { return "" } @@ -291,7 +291,7 @@ func newConsistentHashSelector(servers map[string]string) Selector { return &consistentHashSelector{servers: ss, h: h} } -func (s consistentHashSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { +func (s *consistentHashSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string { ss := s.servers if len(ss) == 0 { return ""