1、grpc负载均衡(resolver)
1.1 proto编写和编译
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) {}
}
$ protoc -I . --go_out=plugins=grpc:. ./echo.proto
1.2 服务端
package mainimport ("context"pb "demo/pb""fmt""google.golang.org/grpc""log""net""sync"
)var (addrs = []string{":50051", ":50052"}
)type ecServer struct {pb.UnimplementedEchoServeraddr 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.WaitGroupfor _, addr := range addrs {wg.Add(1)go func(addr string) {defer wg.Done()startServer(addr)}(addr)}wg.Wait()
}
1.3 客户端
package mainimport ("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.Targetcc resolver.ClientConnaddrsStore map[string][]string
}func (r *exampleResolver) start() {// r.target.Endpoint(): lb.example.grpc.ioaddrStrs := r.addrsStore[r.target.Endpoint()]// len(addrStrs): 2addrs := 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
[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)
# 项目结构
[root@zsx protoc]# tree demo/
demo/
├── client
│ └── client.go
├── go.mod
├── go.sum
├── pb
│ ├── echo.pb.go
│ └── echo.proto
└── server└── server.go3 directories, 6 files
参考地址:
https://godoc.org/google.golang.org/grpc/balancer
https://github.com/grpc/grpc/blob/master/doc/service_config.md