1、grpc负载均衡(resolver)
1.1 proto编写和编译
protobuf
syntax = "proto3";
option go_package = "./;echo";
package echo;
message EchoRequest {
string message = 1;
}
message EchoResponse {
string message = 1;
}
service Echo {
rpc UnaryEcho(EchoRequest) returns (EchoResponse) {}
}
shell
$ protoc -I . --go_out=plugins=grpc:. ./echo.proto
1.2 服务端
go
package main
import (
"context"
pb "demo/pb"
"fmt"
"google.golang.org/grpc"
"log"
"net"
"sync"
)
var (
addrs = []string{":50051", ":50052"}
)
type ecServer struct {
pb.UnimplementedEchoServer
addr string
}
func (s *ecServer) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) {
return &pb.EchoResponse{Message: fmt.Sprintf("%s (from %s)", req.Message, s.addr)}, nil
}
func startServer(addr string) {
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterEchoServer(s, &ecServer{addr: addr})
log.Printf("serving on %s\n", addr)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
var wg sync.WaitGroup
for _, addr := range addrs {
wg.Add(1)
go func(addr string) {
defer wg.Done()
startServer(addr)
}(addr)
}
wg.Wait()
}
1.3 客户端
go
package main
import (
"context"
ecpb "demo/pb"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"log"
"time"
)
const (
exampleScheme = "example"
exampleServiceName = "lb.example.grpc.io"
)
var addrs = []string{"localhost:50051", "localhost:50052"}
func callUnaryEcho(c ecpb.EchoClient, message string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
fmt.Println(r.Message)
}
func makeRPCs(cc *grpc.ClientConn, n int) {
hwc := ecpb.NewEchoClient(cc)
for i := 0; i < n; i++ {
callUnaryEcho(hwc, "this is examples/load_balancing")
}
}
func main() {
// "pick_first" is the default, so there's no need to set the load balancing policy.
pickfirstConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer pickfirstConn.Close()
fmt.Println("--- calling helloworld.Greeter/SayHello with pick_first ---")
makeRPCs(pickfirstConn, 10)
fmt.Println()
// Make another ClientConn with round_robin policy.
roundrobinConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
// This sets the initial balancing policy.
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin":{}}]}`),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer roundrobinConn.Close()
fmt.Println("--- calling helloworld.Greeter/SayHello with round_robin ---")
makeRPCs(roundrobinConn, 10)
}
// Following is an example name resolver implementation. Read the name
// resolution example to learn more about it.
type exampleResolverBuilder struct{}
func (*exampleResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r := &exampleResolver{
target: target,
cc: cc,
addrsStore: map[string][]string{
exampleServiceName: addrs,
},
}
r.start()
return r, nil
}
func (*exampleResolverBuilder) Scheme() string { return exampleScheme }
type exampleResolver struct {
target resolver.Target
cc resolver.ClientConn
addrsStore map[string][]string
}
func (r *exampleResolver) start() {
// r.target.Endpoint(): lb.example.grpc.io
addrStrs := r.addrsStore[r.target.Endpoint()]
// len(addrStrs): 2
addrs := make([]resolver.Address, len(addrStrs))
for i, s := range addrStrs {
addrs[i] = resolver.Address{Addr: s}
}
r.cc.UpdateState(resolver.State{Addresses: addrs})
}
func (*exampleResolver) ResolveNow(o resolver.ResolveNowOptions) {}
func (*exampleResolver) Close() {}
func init() {
resolver.Register(&exampleResolverBuilder{})
}
1.4 测试
[root@zsx demo]# go run server/server.go
2023/02/17 18:32:59 serving on :50052
2023/02/17 18:32:59 serving on :50051
shell
[root@zsx demo]# go run client/client.go
--- calling helloworld.Greeter/SayHello with pick_first ---
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50051)
--- calling helloworld.Greeter/SayHello with round_robin ---
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50052)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50052)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50052)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50052)
this is examples/load_balancing (from :50051)
this is examples/load_balancing (from :50052)
shell
# 项目结构
[root@zsx protoc]# tree demo/
demo/
├── client
│ └── client.go
├── go.mod
├── go.sum
├── pb
│ ├── echo.pb.go
│ └── echo.proto
└── server
└── server.go
3 directories, 6 files
参考地址:
https://godoc.org/google.golang.org/grpc/balancer
https://github.com/grpc/grpc/blob/master/doc/service_config.md