Skip to content

Commit 7d52645

Browse files
Merge pull request #120 from domgoer/master
fix: use unsupported loadbalance will cause nil pointer
2 parents db3f090 + 820804f commit 7d52645

12 files changed

+3183
-841
lines changed

docs/server.md

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Server地址,格式为:"IP:PORT"。
1212
## Protocol
1313
Server的接口协议,目前支持HTTP。
1414

15+
## Weight
16+
Weight 服务器的权重(当该服务器所属的集群负载方式是权重轮询时则需要配置)
17+
1518
## MaxQPS
1619
Server能够支持的最大QPS,用于流控。Gateway采用令牌桶算法,根据QPS限制流量,保护后端Server被压垮。
1720

pkg/client/server.go

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ func (sb *ServerBuilder) MaxQPS(max int64) *ServerBuilder {
8080
return sb
8181
}
8282

83+
// Weight set robin weight
84+
func (sb *ServerBuilder) Weight(weight int64) *ServerBuilder {
85+
sb.value.Weight = weight
86+
return sb
87+
}
88+
8389
// NoCircuitBreaker no circuit breaker
8490
func (sb *ServerBuilder) NoCircuitBreaker() *ServerBuilder {
8591
sb.value.CircuitBreaker = nil

pkg/lb/lb.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,25 @@ var (
1515
// LBS map loadBalance name and process function
1616
LBS = map[metapb.LoadBalance]func() LoadBalance{
1717
metapb.RoundRobin: NewRoundRobin,
18+
metapb.WightRobin: NewWeightRobin,
1819
}
1920
)
2021

21-
// LoadBalance loadBalance interface
22+
// LoadBalance loadBalance interface returns selected server's id
2223
type LoadBalance interface {
23-
Select(req *fasthttp.Request, servers *list.List) int
24+
Select(req *fasthttp.Request, servers *list.List) uint64
2425
}
2526

2627
// GetSupportLBS return supported loadBalances
2728
func GetSupportLBS() []metapb.LoadBalance {
2829
return supportLbs
2930
}
3031

31-
// NewLoadBalance create a LoadBalance
32+
// NewLoadBalance create a LoadBalance,if LoadBalance function is not supported
33+
// it will return NewRoundRobin
3234
func NewLoadBalance(name metapb.LoadBalance) LoadBalance {
33-
return LBS[name]()
35+
if l, ok := LBS[name]; ok {
36+
return l()
37+
}
38+
return NewRoundRobin()
3439
}

pkg/lb/roundrobin.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package lb
22

33
import (
44
"container/list"
5+
"github.com/fagongzi/gateway/pkg/pb/metapb"
6+
"github.com/fagongzi/util/collection"
57
"sync/atomic"
68

79
"github.com/valyala/fasthttp"
@@ -23,12 +25,19 @@ func NewRoundRobin() LoadBalance {
2325
}
2426

2527
// Select select a server from servers using RoundRobin
26-
func (rr RoundRobin) Select(req *fasthttp.Request, servers *list.List) int {
28+
func (rr RoundRobin) Select(req *fasthttp.Request, servers *list.List) uint64 {
2729
l := uint64(servers.Len())
2830

2931
if 0 >= l {
30-
return -1
32+
return 0
3133
}
3234

33-
return int(atomic.AddUint64(rr.ops, 1) % l)
35+
idx := int(atomic.AddUint64(rr.ops, 1) % l)
36+
37+
v := collection.Get(servers, idx).Value
38+
if v == nil {
39+
return 0
40+
}
41+
42+
return v.(*metapb.Server).ID
3443
}

pkg/lb/weightrobin.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package lb
2+
3+
import (
4+
"container/list"
5+
"github.com/fagongzi/gateway/pkg/pb/metapb"
6+
"github.com/valyala/fasthttp"
7+
)
8+
9+
// WeightRobin weight robin loadBalance impl
10+
type WeightRobin struct {
11+
opts map[uint64]*weightRobin
12+
}
13+
14+
// weightRobin used to save the weight info of server
15+
type weightRobin struct {
16+
effectiveWeight int64
17+
currentWeight int64
18+
}
19+
20+
// NewWeightRobin create a WeightRobin
21+
func NewWeightRobin() LoadBalance {
22+
return &WeightRobin{
23+
opts: make(map[uint64]*weightRobin, 1024),
24+
}
25+
}
26+
27+
// Select select a server from servers using WeightRobin
28+
func (w *WeightRobin) Select(req *fasthttp.Request, servers *list.List) (best uint64) {
29+
var total int64
30+
31+
for iter := servers.Back(); iter != nil; iter = iter.Prev() {
32+
svr := iter.Value.(*metapb.Server)
33+
34+
id := svr.ID
35+
if _, ok := w.opts[id]; !ok {
36+
w.opts[id] = &weightRobin{
37+
effectiveWeight: svr.Weight,
38+
}
39+
}
40+
41+
wt := w.opts[id]
42+
wt.currentWeight += wt.effectiveWeight
43+
total += wt.effectiveWeight
44+
45+
if wt.effectiveWeight < svr.Weight {
46+
wt.effectiveWeight++
47+
}
48+
49+
if best == 0 || w.opts[uint64(best)] == nil || wt.currentWeight > w.opts[best].currentWeight {
50+
best = id
51+
}
52+
}
53+
54+
if best == 0 {
55+
return 0
56+
}
57+
58+
w.opts[best].currentWeight -= total
59+
60+
return best
61+
}

pkg/lb/weightrobin_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package lb
2+
3+
import (
4+
"container/list"
5+
"github.com/fagongzi/gateway/pkg/pb/metapb"
6+
"github.com/valyala/fasthttp"
7+
"testing"
8+
)
9+
10+
func TestWeightRobin_Select(t *testing.T) {
11+
li := list.New()
12+
13+
li.PushBack(&metapb.Server{
14+
ID: 1,
15+
Weight: 20,
16+
})
17+
li.PushBack(&metapb.Server{
18+
ID: 2,
19+
Weight: 10,
20+
})
21+
li.PushBack(&metapb.Server{
22+
ID: 3,
23+
Weight: 35,
24+
})
25+
li.PushBack(&metapb.Server{
26+
ID: 4,
27+
Weight: 5,
28+
})
29+
30+
type fields struct {
31+
opts map[uint64]*weightRobin
32+
}
33+
type args struct {
34+
req *fasthttp.Request
35+
servers *list.List
36+
}
37+
tests := []struct {
38+
name string
39+
fields fields
40+
args args
41+
wantBest []int
42+
}{
43+
{
44+
name: "test_case_1",
45+
fields: struct{ opts map[uint64]*weightRobin }{opts: make(map[uint64]*weightRobin, 50)},
46+
args: struct {
47+
req *fasthttp.Request
48+
servers *list.List
49+
}{req: nil, servers: li},
50+
wantBest: []int{20, 10, 35, 5},
51+
},
52+
}
53+
for _, tt := range tests {
54+
var res = make(map[uint64]int)
55+
t.Run(tt.name, func(t *testing.T) {
56+
w := &WeightRobin{
57+
opts: tt.fields.opts,
58+
}
59+
for i := 0; i < 70; i++ {
60+
res[w.Select(tt.args.req, tt.args.servers)]++
61+
}
62+
})
63+
for k, v := range res {
64+
if tt.wantBest[k-1] != v {
65+
t.Errorf("WeightRobin.Select() = %v, want %v", res, tt.wantBest)
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)