做网站需要什么编程语言,海外推广电商,旅游网站开发的目的,创建企业网站经过哪些步骤1、grpc resolver
当我们的服务刚刚成型时#xff0c;可能一个服务只有一台实例#xff0c;这时候client要建立grpc连接很简单#xff0c;只需要指定server
的ip就可以了。但是#xff0c;当服务成熟了#xff0c;业务量大了#xff0c;这个时候#xff0c;一个实例就…1、grpc resolver
当我们的服务刚刚成型时可能一个服务只有一台实例这时候client要建立grpc连接很简单只需要指定server
的ip就可以了。但是当服务成熟了业务量大了这个时候一个实例就就不够用了我们需要部署一个服务集
群。一个集群有很多实例且可以随时的扩容部分实例出现了故障也没关系这样就提升了服务的处理能力和稳
定性但是也带来一个问题grpc的client如何和这个集群里的server建立连接
这个问题可以一分为二第一个问题如何根据服务名称返回实例的ip这个问题有很多种解决方案我们可以
使用一些成熟的服务发现组件例如consul或者zookeeper也可以我们自己实现一个解析服务器第二个问题
如何将我们选择的服务解析方式应用到grpc的连接建立中去这个也不难因为grpc的resolver就是帮我们解决
这个问题的本篇我们就来探讨一下grpc的resolver是如何使用的。
1.1 proto编写和编译
syntax proto3;
package helloworld;
option go_package ./;helloworld;service Greeter {rpc SayHello (HelloRequest) returns (HelloReply) {}
}message HelloRequest {string name 1;
}message HelloReply {string message 1;
}$ protoc -I . --go_outpluginsgrpc:. ./helloword.proto1.2 服务端编写
这里需要编写两个服务端
package mainimport (contextpb demo/pbgoogle.golang.org/grpclognet
)const (port :50051
)type server struct {pb.UnimplementedGreeterServer
}func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {// 打印客户端传入HelloRequest请求的Name参数log.Printf(Received: %v, in.GetName())// 将name参数作为返回值,返回给客户端return pb.HelloReply{Message: Service1: Hello in.GetName()}, nil
}// main方法 函数开始执行的地方
func main() {// 调用标准库监听50051端口的tcp连接lis, err : net.Listen(tcp, port)if err ! nil {log.Fatalf(failed to listen: %v, err)}log.Println(listen: , port)// 创建grpc服务s : grpc.NewServer()// 将server对象,也就是实现SayHello方法的对象,与grpc服务绑定pb.RegisterGreeterServer(s, server{})// grpc服务开始接收访问50051端口的tcp连接数据if err : s.Serve(lis); err ! nil {log.Fatalf(failed to serve: %v, err)}
}package mainimport (contextpb demo/pbgoogle.golang.org/grpclognet
)const (port2 :50052
)type server2 struct {pb.UnimplementedGreeterServer
}func (s *server2) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {// 打印客户端传入HelloRequest请求的Name参数log.Printf(Received: %v, in.GetName())// 将name参数作为返回值返回给客户端return pb.HelloReply{Message: Service2: Hello in.GetName()}, nil
}// main方法 函数开始执行的地方
func main() {// 调用标准库监听50052端口的tcp连接lis, err : net.Listen(tcp, port2)if err ! nil {log.Fatalf(failed to listen: %v, err)}log.Println(listen: ,port2)//创建grpc服务s : grpc.NewServer()//将server对象也就是实现SayHello方法的对象与grpc服务绑定pb.RegisterGreeterServer(s, server2{})// grpc服务开始接收访问50052端口的tcp连接数据if err : s.Serve(lis); err ! nil {log.Fatalf(failed to serve: %v, err)}
}1.3 客户端编写
package mainimport (contextpb demo/pbgoogle.golang.org/grpcgoogle.golang.org/grpc/resolverlogtime
)// 全局注册Scheme为myservice的Resolver Build
func init() {resolver.Register(myServiceBuilder{})
}type myServiceBuilder struct {
}func (*myServiceBuilder) Scheme() string {return myservice
}// 创建Resolver实例
func (*myServiceBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {r : myServiceResolver{target: target,cc: cc,}r.start()return r, nil
}type myServiceResolver struct {target resolver.Targetcc resolver.ClientConn
}// 根据target不同解析出不同的端口
func (r *myServiceResolver) start() {if r.target.Endpoint() abc {r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: :50051}}})} else if r.target.Endpoint() efg {r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: :50052}}})}
}// 再次解析使用的解析方式不变
func (r *myServiceResolver) ResolveNow(o resolver.ResolveNowOptions) {r.start()
}func (*myServiceResolver) Close() {}const (address1 myservice:///abcaddress2 myservice:///efg
)func main() {// myservice:///abc// 访问服务端address,创建连接conn,地址格式myservice:///abcconn, err : grpc.Dial(address1, grpc.WithInsecure(), grpc.WithBlock())if err ! nil {log.Fatalf(did not connect: %v, err)}defer conn.Close()c : pb.NewGreeterClient(conn)// 设置客户端访问超时时间1秒ctx, cancel : context.WithTimeout(context.Background(), time.Second)defer cancel()// 客户端调用服务端 SayHello 请求传入Name 为 world, 返回值为服务端返回参数r, err : c.SayHello(ctx, pb.HelloRequest{Name: world})if err ! nil {log.Fatalf(could not greet: %v, err)}// 根据服务端处理逻辑返回值也为worldlog.Printf(Greeting: %s, r.GetMessage())// myservice:///efgconn2, err2 : grpc.Dial(address2, grpc.WithInsecure(), grpc.WithBlock())if err2 ! nil {log.Fatalf(did not connect: %v, err)}defer conn2.Close()c2 : pb.NewGreeterClient(conn2)// 设置客户端访问超时时间1秒ctx2, cancel2 : context.WithTimeout(context.Background(), time.Second)defer cancel2()// 客户端调用服务端 SayHello 请求传入Name 为 world, 返回值为服务端返回参数r2, err2 : c2.SayHello(ctx2, pb.HelloRequest{Name: world})if err2 ! nil {log.Fatalf(could not greet: %v, err2)}// 根据服务端处理逻辑返回值也为worldlog.Printf(Greeting: %s, r2.GetMessage())
}1.4 测试
[rootzsx demo]# go run server/server1.go
2023/02/17 14:18:06 listen: :50051
2023/02/17 14:18:32 Received: world[rootzsx demo]# go run server/server2.go
2023/02/17 14:18:17 listen: :50052
2023/02/17 14:18:32 Received: world[rootzsx demo]# go run client/client.go
2023/02/17 14:18:32 Greeting: Service1: Hello world
2023/02/17 14:18:32 Greeting: Service2: Hello world# 项目结构
$ tree demo/
demo/
├── client
│ └── client.go
├── go.mod
├── go.sum
├── pb
│ ├── helloword.pb.go
│ └── helloword.proto
└── server├── server1.go└── server2.go3 directories, 7 files