grpc 跟常见的client-server模型相似(doubbo)
grpc 编码之前需要准备以下环境:
- 安装protobuf,grpc的client与server之间消息传递使用的protoc格式消息,比起json,xml速度快
- 安装grpc 的源码包
下面开始编写grpc示例代码:
- 首先编写proto文件,示例:helloworld
syntax = "proto3"; option objc_class_prefix = "HLW"; package helloworld; // 定义一个Greeter服务,其中API为SayHello // 形式参数: HelloRequest // 返回参数:HelloReply service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} // rpc 借口的类型分为一下四种: A为接受参数,B为返回参数 // 1. rpc GetFeature(Point) returns (Feature) {} 普通调用:A-B // 2. rpc ListFeatures(Rectangle) returns (stream Feature) {} 单向流:A - B(流) // 3. rpc RecordRoute(stream Point) returns (RouteSummary) {} 单向流:A(流) - B // 4. rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} 双向流:A(流) - B(流) } // 请求参数-根据自己的需求定义 message HelloRequest { string name = 1; } // 返回参数-根据自己的需求定义 message HelloReply { string message = 1; }
2. 将helloworld.proto转化为helloworld.proto.go
命令:protoc --go_out=plugins=grpc:. helloworld.proto 结果如下:
// Code generated by protoc-gen-go. // source: helloworld.proto // DO NOT EDIT! /* Package helloworld is a generated protocol buffer package. It is generated from these files: helloworld.proto It has these top-level messages: HelloRequest HelloReply */ package myblogproto import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" import ( context "golang.org/x/net/context" grpc "google.golang.org/grpc" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // 请求参数-根据自己的需求定义 type HelloRequest struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` } func (m *HelloRequest) Reset() { *m = HelloRequest{} } func (m *HelloRequest) String() string { return proto.CompactTextString(m) } func (*HelloRequest) ProtoMessage() {} func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (m *HelloRequest) GetName() string { if m != nil { return m.Name } return "" } // 返回参数-根据自己的需求定义 type HelloReply struct { Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` } func (m *HelloReply) Reset() { *m = HelloReply{} } func (m *HelloReply) String() string { return proto.CompactTextString(m) } func (*HelloReply) ProtoMessage() {} func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } func (m *HelloReply) GetMessage() string { if m != nil { return m.Message } return "" } func init() { proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 // Client API for Greeter service type GreeterClient interface { // Sends a greeting SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) } type greeterClient struct { cc *grpc.ClientConn } func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { return &greeterClient{cc} } func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { out := new(HelloReply) err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...) if err != nil { return nil, err } return out, nil } // Server API for Greeter service type GreeterServer interface { // Sends a greeting SayHello(context.Context, *HelloRequest) (*HelloReply, error) } func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { s.RegisterService(&_Greeter_serviceDesc, srv) } func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HelloRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(GreeterServer).SayHello(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/helloworld.Greeter/SayHello", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) } return interceptor(ctx, in, info, handler) } var _Greeter_serviceDesc = grpc.ServiceDesc{ ServiceName: "helloworld.Greeter", HandlerType: (*GreeterServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "SayHello", Handler: _Greeter_SayHello_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "helloworld.proto", } func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 149 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88, 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a, 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64, 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0xb1, 0x2d, 0x62, 0x62, 0xf6, 0xf0, 0x09, 0x4f, 0x62, 0x03, 0xbb, 0xd8, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xbe, 0xde, 0x1d, 0x2e, 0xc5, 0x00, 0x00, 0x00, }
3. 编写grpc服务端,伪代码:
package main import ( "fmt" "golang.org/x/net/context" "google.golang.org/grpc" "net" pb "unicontract-validate/tests/grpcStudy/mybloggrpc/myblogproto" ) const port = ":50051" // 定义struct实现我们自定义的helloworld.proto对应的服务 type myServer struct { } func (m *myServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{"请求server端成功!"}, nil } /** 1. 首先我们必须实现我们自定义rpc服务,例如:rpc SayHello()-在此我们可以实现我们自己的逻辑 2. 创建监听listener 3. 创建grpc的服务 4. 将我们的服务注册到grpc的server中 5. 启动grpc服务,将我们自定义的监听信息传递给grpc服务器 */ func main() { // 创建server端监听端口 list, err := net.Listen("tcp", port) if err != nil { fmt.Println(err) } // 创建grpc的server server := grpc.NewServer() // 注册我们自定义的helloworld服务 pb.RegisterGreeterServer(server, &myServer{}) // 启动grpc服务 fmt.Println("grpc 服务启动... ...") server.Serve(list) }
4. 编写client端代码,伪代码如下:
package main import ( "google.golang.org/grpc" "fmt" "context" pb "unicontract-validate/tests/grpcStudy/mybloggrpc/myblogproto" ) // 此处应与服务器端对应 const address = "127.0.0.1:50051" /** 1. 创建groc连接器 2. 创建grpc客户端,并将连接器赋值给客户端 3. 向grpc服务端发起请求 4. 获取grpc服务端返回的结果 */ func main() { // 创建一个grpc连接器 conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil{ fmt.Println(err) } // 当请求完毕后记得关闭连接,否则大量连接会占用资源 defer conn.Close() // 创建grpc客户端 c := pb.NewGreeterClient(conn) name := "我是客户端,正在请求服务端!!!" // 客户端向grpc服务端发起请求 result, err := c.SayHello(context.Background(), &pb.HelloRequest{Name:name}) fmt.Println(name) if err != nil{ fmt.Println("请求失败!!!") return } // 获取服务端返回的结果 fmt.Println(result.Message) }
服务端结果:
客户端请求之后结果: