Skip to content

Commit 2197c7b

Browse files
authored
example: load_balancing (#2504)
1 parent 0a3dc64 commit 2197c7b

File tree

3 files changed

+286
-0
lines changed

3 files changed

+286
-0
lines changed

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

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Load balancing
2+
3+
This examples shows how `ClientConn` can pick different load balancing policies.
4+
5+
Note: to show the effect of load balancers, an example resolver is installed in
6+
this example to get the backend addresses. It's suggested to read the name
7+
resolver example before this example.
8+
9+
## Try it
10+
11+
```
12+
go run server/main.go
13+
```
14+
15+
```
16+
go run client/main.go
17+
```
18+
19+
## Explanation
20+
21+
Two echo servers are serving on ":50051" and ":50052". They will include their
22+
serving address in the response. So the server on ":50051" will reply to the RPC
23+
with `this is examples/load_balancing (from :50051)`.
24+
25+
Two clients are created, to connect to both of these servers (they get both
26+
server addresses from the name resolver).
27+
28+
Each client picks a different load balancer (using `grpc.WithBalancerName`):
29+
`pick_first` or `round_robin`. (These two policies are supported in gRPC by
30+
default. To add a custom balancing policy, implement the interfaces defined in
31+
https://godoc.org/google.golang.org/grpc/balancer).
32+
33+
Note that balancers can also be switched using service config, which allows
34+
service owners (instead of client owners) to pick the balancer to use. Service
35+
config doc is available at
36+
https://github.com/grpc/grpc/blob/master/doc/service_config.md.
37+
38+
### pick_first
39+
40+
The first client is configured to use `pick_first`. `pick_first` tries to
41+
connect to the first address, uses it for all RPCs if it connects, or try the
42+
next address if it fails (and keep doing that until one connection is
43+
successful). Because of this, all the RPCs will be sent to the same backend. The
44+
responses received all show the same backend address.
45+
46+
```
47+
this is examples/load_balancing (from :50051)
48+
this is examples/load_balancing (from :50051)
49+
this is examples/load_balancing (from :50051)
50+
this is examples/load_balancing (from :50051)
51+
this is examples/load_balancing (from :50051)
52+
this is examples/load_balancing (from :50051)
53+
this is examples/load_balancing (from :50051)
54+
this is examples/load_balancing (from :50051)
55+
this is examples/load_balancing (from :50051)
56+
this is examples/load_balancing (from :50051)
57+
```
58+
59+
### round_robin
60+
61+
The second client is configured to use `round_robin`. `round_robin` connects to
62+
all the addresses it sees, and sends an RPC to each backend one at a time in
63+
order. E.g. the first RPC will be sent to backend-1, the second RPC will be be
64+
sent to backend-2, and the third RPC will be be sent to backend-1 again.
65+
66+
```
67+
this is examples/load_balancing (from :50051)
68+
this is examples/load_balancing (from :50051)
69+
this is examples/load_balancing (from :50052)
70+
this is examples/load_balancing (from :50051)
71+
this is examples/load_balancing (from :50052)
72+
this is examples/load_balancing (from :50051)
73+
this is examples/load_balancing (from :50052)
74+
this is examples/load_balancing (from :50051)
75+
this is examples/load_balancing (from :50052)
76+
this is examples/load_balancing (from :50051)
77+
```
78+
79+
Note that it's possible to see two continues RPC sent to the same backend.
80+
That's because `round_robin` only picks the connections ready for RPCs. So if
81+
one of the two connections is not ready for some reason, all RPCs will be sent
82+
to the ready connection.

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

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
// Binary client is an example client.
20+
package main
21+
22+
import (
23+
"context"
24+
"fmt"
25+
"log"
26+
"time"
27+
28+
"google.golang.org/grpc"
29+
ecpb "google.golang.org/grpc/examples/features/proto/echo"
30+
"google.golang.org/grpc/resolver"
31+
)
32+
33+
const (
34+
exampleScheme = "example"
35+
exampleServiceName = "lb.example.grpc.io"
36+
)
37+
38+
var addrs = []string{"localhost:50051", "localhost:50052"}
39+
40+
func callUnaryEcho(c ecpb.EchoClient, message string) {
41+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
42+
defer cancel()
43+
r, err := c.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
44+
if err != nil {
45+
log.Fatalf("could not greet: %v", err)
46+
}
47+
fmt.Println(r.Message)
48+
}
49+
50+
func makeRPCs(cc *grpc.ClientConn, n int) {
51+
hwc := ecpb.NewEchoClient(cc)
52+
for i := 0; i < n; i++ {
53+
callUnaryEcho(hwc, "this is examples/load_balancing")
54+
}
55+
}
56+
57+
func main() {
58+
pickfirstConn, err := grpc.Dial(
59+
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
60+
// grpc.WithBalancerName("pick_first"), // "pick_first" is the default, so this DialOption is not necessary.
61+
grpc.WithInsecure(),
62+
)
63+
if err != nil {
64+
log.Fatalf("did not connect: %v", err)
65+
}
66+
defer pickfirstConn.Close()
67+
68+
fmt.Println("--- calling helloworld.Greeter/SayHello with pick_first ---")
69+
makeRPCs(pickfirstConn, 10)
70+
71+
fmt.Println()
72+
73+
// Make another ClientConn with round_robin policy.
74+
roundrobinConn, err := grpc.Dial(
75+
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
76+
grpc.WithBalancerName("round_robin"), // This sets the initial balancing policy.
77+
grpc.WithInsecure(),
78+
)
79+
if err != nil {
80+
log.Fatalf("did not connect: %v", err)
81+
}
82+
defer roundrobinConn.Close()
83+
84+
fmt.Println("--- calling helloworld.Greeter/SayHello with round_robin ---")
85+
makeRPCs(roundrobinConn, 10)
86+
}
87+
88+
// Following is an example name resolver implementation. Read the name
89+
// resolution example to learn more about it.
90+
91+
type exampleResolverBuilder struct{}
92+
93+
func (*exampleResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
94+
r := &exampleResolver{
95+
target: target,
96+
cc: cc,
97+
addrsStore: map[string][]string{
98+
exampleServiceName: addrs,
99+
},
100+
}
101+
r.start()
102+
return r, nil
103+
}
104+
func (*exampleResolverBuilder) Scheme() string { return exampleScheme }
105+
106+
type exampleResolver struct {
107+
target resolver.Target
108+
cc resolver.ClientConn
109+
addrsStore map[string][]string
110+
}
111+
112+
func (r *exampleResolver) start() {
113+
addrStrs := r.addrsStore[r.target.Endpoint]
114+
addrs := make([]resolver.Address, len(addrStrs), len(addrStrs))
115+
for i, s := range addrStrs {
116+
addrs[i] = resolver.Address{Addr: s}
117+
}
118+
r.cc.NewAddress(addrs)
119+
}
120+
func (*exampleResolver) ResolveNow(o resolver.ResolveNowOption) {}
121+
func (*exampleResolver) Close() {}
122+
123+
func init() {
124+
resolver.Register(&exampleResolverBuilder{})
125+
}

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

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
// Binary server is an example server.
20+
package main
21+
22+
import (
23+
"context"
24+
"fmt"
25+
"log"
26+
"net"
27+
"sync"
28+
29+
"google.golang.org/grpc"
30+
"google.golang.org/grpc/codes"
31+
ecpb "google.golang.org/grpc/examples/features/proto/echo"
32+
"google.golang.org/grpc/status"
33+
)
34+
35+
var (
36+
addrs = []string{":50051", ":50052"}
37+
)
38+
39+
type ecServer struct {
40+
addr string
41+
}
42+
43+
func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) {
44+
return &ecpb.EchoResponse{Message: fmt.Sprintf("%s (from %s)", req.Message, s.addr)}, nil
45+
}
46+
func (s *ecServer) ServerStreamingEcho(*ecpb.EchoRequest, ecpb.Echo_ServerStreamingEchoServer) error {
47+
return status.Errorf(codes.Unimplemented, "not implemented")
48+
}
49+
func (s *ecServer) ClientStreamingEcho(ecpb.Echo_ClientStreamingEchoServer) error {
50+
return status.Errorf(codes.Unimplemented, "not implemented")
51+
}
52+
func (s *ecServer) BidirectionalStreamingEcho(ecpb.Echo_BidirectionalStreamingEchoServer) error {
53+
return status.Errorf(codes.Unimplemented, "not implemented")
54+
}
55+
56+
func startServer(addr string) {
57+
lis, err := net.Listen("tcp", addr)
58+
if err != nil {
59+
log.Fatalf("failed to listen: %v", err)
60+
}
61+
s := grpc.NewServer()
62+
ecpb.RegisterEchoServer(s, &ecServer{addr: addr})
63+
log.Printf("serving on %s\n", addr)
64+
if err := s.Serve(lis); err != nil {
65+
log.Fatalf("failed to serve: %v", err)
66+
}
67+
}
68+
69+
func main() {
70+
var wg sync.WaitGroup
71+
for _, addr := range addrs {
72+
wg.Add(1)
73+
go func(addr string) {
74+
defer wg.Done()
75+
startServer(addr)
76+
}(addr)
77+
}
78+
wg.Wait()
79+
}

0 commit comments

Comments
 (0)