Skip to content

Commit cdfebf6

Browse files
committed
example: load_balancing
Show how to configure RR
1 parent 272e9d4 commit cdfebf6

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

Diff for: examples/features/load_balancing/README.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Load balancing
2+
3+
This examples shows how `ClientConn` can pick different load balancing policies.
4+
5+
## Try it
6+
7+
```
8+
go run server/main.go
9+
```
10+
11+
```
12+
go run client/main.go
13+
```
14+
15+
## Explanation
16+
17+
Two helloworld servers are serving on ":50051" and ":50052". They will include
18+
their serving address in the response. So the server on ":50051" will reply to
19+
the RPC with `hello lb (from :50051)`.
20+
21+
Two clients are created, to connect to both of these servers (they get both
22+
server addresses from the name resolver).
23+
24+
### pick_first
25+
26+
The first client is configured to use `pick_first`. `pick_first` tries to
27+
connect to the first address, uses it for all RPCs if it connects, or try the
28+
next address if it fails (and keep doing that until one connection is
29+
successful). Because of this, all the RPCs will be sent to the same backend. The
30+
responses received all show the same backend address.
31+
32+
```
33+
hello lb (from :50051)
34+
hello lb (from :50051)
35+
hello lb (from :50051)
36+
hello lb (from :50051)
37+
hello lb (from :50051)
38+
hello lb (from :50051)
39+
hello lb (from :50051)
40+
hello lb (from :50051)
41+
hello lb (from :50051)
42+
hello lb (from :50051)
43+
```
44+
45+
### round_robin
46+
47+
The second client is configured to use `round_robin`. `round_robin` connects to
48+
all the addresses it sees, and sends an RPC to each backend one at a time in
49+
order. E.g. the first RPC will be sent to backend-1, the second RPC will be be
50+
sent to backend-2, and the third RPC will be be sent to backend-1 again.
51+
52+
```
53+
hello lb (from :50051)
54+
hello lb (from :50051)
55+
hello lb (from :50052)
56+
hello lb (from :50051)
57+
hello lb (from :50052)
58+
hello lb (from :50051)
59+
hello lb (from :50052)
60+
hello lb (from :50051)
61+
hello lb (from :50052)
62+
hello lb (from :50051)
63+
```
64+
65+
Note that it's possible to see two continues RPC sent to the same backend.
66+
That's because `round_robin` only picks the connections ready for RPCs. So if
67+
one of the two connections is not ready for some reason, all RPCs will be sent
68+
to the ready connection.

Diff for: examples/features/load_balancing/client/main.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
*
3+
* Copyright 2018 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package main
20+
21+
import (
22+
"context"
23+
"fmt"
24+
"log"
25+
"strings"
26+
"time"
27+
28+
"google.golang.org/grpc"
29+
hwpb "google.golang.org/grpc/examples/helloworld/helloworld"
30+
"google.golang.org/grpc/resolver"
31+
)
32+
33+
const splitScheme = "split"
34+
35+
var addrs = []string{"localhost:50051", "localhost:50052"}
36+
37+
type splitResolverBuilder struct{}
38+
39+
func (*splitResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
40+
r := &splitResolver{target: target, cc: cc}
41+
r.start()
42+
return r, nil
43+
}
44+
func (*splitResolverBuilder) Scheme() string { return splitScheme }
45+
46+
type splitResolver struct {
47+
target resolver.Target
48+
cc resolver.ClientConn
49+
}
50+
51+
func (r *splitResolver) start() {
52+
splitted := strings.Split(r.target.Endpoint, ",")
53+
addrs := make([]resolver.Address, len(splitted), len(splitted))
54+
for i, s := range splitted {
55+
addrs[i] = resolver.Address{Addr: s}
56+
}
57+
r.cc.NewAddress(addrs)
58+
}
59+
func (*splitResolver) ResolveNow(o resolver.ResolveNowOption) {}
60+
func (*splitResolver) Close() {}
61+
62+
func init() { resolver.Register(&splitResolverBuilder{}) }
63+
64+
///////////////////////////////////////////////////////////////////////
65+
66+
// callSayHello calls SayHello on c with the given name, and prints the
67+
// response.
68+
func callSayHello(c hwpb.GreeterClient, name string) {
69+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
70+
defer cancel()
71+
r, err := c.SayHello(ctx, &hwpb.HelloRequest{Name: name})
72+
if err != nil {
73+
log.Fatalf("could not greet: %v", err)
74+
}
75+
fmt.Println(r.Message)
76+
}
77+
78+
func makeRPCs(cc *grpc.ClientConn, n int) {
79+
hwc := hwpb.NewGreeterClient(cc)
80+
for i := 0; i < n; i++ {
81+
callSayHello(hwc, "lb")
82+
}
83+
}
84+
85+
func main() {
86+
pickfirstConn, err := grpc.Dial(
87+
fmt.Sprintf("%s:///%s", splitScheme, strings.Join(addrs, ",")),
88+
// grpc.WithBalancerName("pick_first"), // "pick_first" is the default, so this DialOption is not necessary.
89+
grpc.WithInsecure(),
90+
)
91+
if err != nil {
92+
log.Fatalf("did not connect: %v", err)
93+
}
94+
defer pickfirstConn.Close()
95+
96+
fmt.Println("--- calling helloworld.Greeter/SayHello with pick_first ---")
97+
makeRPCs(pickfirstConn, 10)
98+
99+
fmt.Println()
100+
101+
// Make another ClientConn with round_robin policy.
102+
roundrobinConn, err := grpc.Dial(
103+
fmt.Sprintf("%s:///%s", splitScheme, strings.Join(addrs, ",")),
104+
grpc.WithBalancerName("round_robin"), // This sets the initial balancing policy.
105+
grpc.WithInsecure(),
106+
)
107+
if err != nil {
108+
log.Fatalf("did not connect: %v", err)
109+
}
110+
defer roundrobinConn.Close()
111+
112+
fmt.Println("--- calling helloworld.Greeter/SayHello with round_robin ---")
113+
makeRPCs(roundrobinConn, 10)
114+
}

Diff for: examples/features/load_balancing/server/main.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
*
3+
* Copyright 2018 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package main
20+
21+
import (
22+
"context"
23+
"fmt"
24+
"log"
25+
"net"
26+
"sync"
27+
28+
"google.golang.org/grpc"
29+
hwpb "google.golang.org/grpc/examples/helloworld/helloworld"
30+
)
31+
32+
var (
33+
addrs = []string{":50051", ":50052"}
34+
)
35+
36+
// hwServer is used to implement helloworld.GreeterServer.
37+
type hwServer struct {
38+
addr string
39+
}
40+
41+
// SayHello implements helloworld.GreeterServer
42+
func (s *hwServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) {
43+
return &hwpb.HelloReply{Message: fmt.Sprintf("hello %s (from %s)", in.Name, s.addr)}, nil
44+
}
45+
46+
func startServer(addr string) {
47+
lis, err := net.Listen("tcp", addr)
48+
if err != nil {
49+
log.Fatalf("failed to listen: %v", err)
50+
}
51+
s := grpc.NewServer()
52+
hwpb.RegisterGreeterServer(s, &hwServer{addr: addr})
53+
log.Printf("serving on %s\n", addr)
54+
if err := s.Serve(lis); err != nil {
55+
log.Fatalf("failed to serve: %v", err)
56+
}
57+
}
58+
59+
func main() {
60+
var wg sync.WaitGroup
61+
for _, addr := range addrs {
62+
wg.Add(1)
63+
go func(addr string) {
64+
defer wg.Done()
65+
startServer(addr)
66+
}(addr)
67+
}
68+
wg.Wait()
69+
}

0 commit comments

Comments
 (0)