grpc开发实战
一、Python下grpc的开发
1、简介
在之前的Protocol Buffers学习指南中说明了与GRPC的配合,需要先进行安装相关的工具:
python -m pip install grpcio #安装grpc python -m pip install grpcio-tools #安装grpc tools
在Protocol Buffers官方文档中说到过如何进行rpc的配置,如果您想在 RPC(远程过程调用)系统中使用您的message类型,您可以在一个.proto
文件中定义一个 RPC 服务接口,并且protocol buffer编译器将以您选择的语言生成服务接口代码和存根。因此,例如,如果你想定义一个 RPC 服务,它的方法接受你的SearchRequest并返回 一个SearchResponse
,你可以在你的
.proto
文件中定义它,如下所示:
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
与protocol buffer一起使用的最直接的 RPC 系统是gRPC:由 Google 开发的一种语言和平台中立的开源 RPC 系统。gRPC 特别适用于protocol buffer,并允许你的.proto文件
使用特殊的protocol buffer编译器插件直接从文件中生成相关的 RPC 代码。
2、开发
- hello.proto
syntax = "proto3"; option go_package = "common/v1"; service Greeter { rpc SayHello(HelloRequest) returns (HelloResponse); } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; }
此时需要通过proto文件生成两份python文件,hello_pb2.py以及hello_pb2_grpc.py,生成文件的命令:
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. hello.proto
- hello_pb2.py
# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: hello.proto """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='hello.proto', package='', syntax='proto3', serialized_options=b'Z\tcommon/v1', create_key=_descriptor._internal_create_key, serialized_pb=b'\n\x0bhello.proto\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\" \n\rHelloResponse\x12\x0f\n\x07message\x18\x01 \x01(\t24\n\x07Greeter\x12)\n\x08SayHello\x12\r.HelloRequest\x1a\x0e.HelloResponseB\x0bZ\tcommon/v1b\x06proto3' ) _HELLOREQUEST = _descriptor.Descriptor( name='HelloRequest', full_name='HelloRequest', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( name='name', full_name='HelloRequest.name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=15, serialized_end=43, ) _HELLORESPONSE = _descriptor.Descriptor( name='HelloResponse', full_name='HelloResponse', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( name='message', full_name='HelloResponse.message', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=45, serialized_end=77, ) DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST DESCRIPTOR.message_types_by_name['HelloResponse'] = _HELLORESPONSE _sym_db.RegisterFileDescriptor(DESCRIPTOR) HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), { 'DESCRIPTOR' : _HELLOREQUEST, '__module__' : 'hello_pb2' # @@protoc_insertion_point(class_scope:HelloRequest) }) _sym_db.RegisterMessage(HelloRequest) HelloResponse = _reflection.GeneratedProtocolMessageType('HelloResponse', (_message.Message,), { 'DESCRIPTOR' : _HELLORESPONSE, '__module__' : 'hello_pb2' # @@protoc_insertion_point(class_scope:HelloResponse) }) _sym_db.RegisterMessage(HelloResponse) DESCRIPTOR._options = None _GREETER = _descriptor.ServiceDescriptor( name='Greeter', full_name='Greeter', file=DESCRIPTOR, index=0, serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=79, serialized_end=131, methods=[ _descriptor.MethodDescriptor( name='SayHello', full_name='Greeter.SayHello', index=0, containing_service=None, input_type=_HELLOREQUEST, output_type=_HELLORESPONSE, serialized_options=None, create_key=_descriptor._internal_create_key, ), ]) _sym_db.RegisterServiceDescriptor(_GREETER) DESCRIPTOR.services_by_name['Greeter'] = _GREETER # @@protoc_insertion_point(module_scope)
- hello_pb2_grpc.py
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc from . import hello_pb2 as hello__pb2 class GreeterStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): """Constructor. Args: channel: A grpc.Channel. """ self.SayHello = channel.unary_unary( '/Greeter/SayHello', request_serializer=hello__pb2.HelloRequest.SerializeToString, response_deserializer=hello__pb2.HelloResponse.FromString, ) class GreeterServicer(object): """Missing associated documentation comment in .proto file.""" def SayHello(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def add_GreeterServicer_to_server(servicer, server): rpc_method_handlers = { 'SayHello': grpc.unary_unary_rpc_method_handler( servicer.SayHello, request_deserializer=hello__pb2.HelloRequest.FromString, response_serializer=hello__pb2.HelloResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'Greeter', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. class Greeter(object): """Missing associated documentation comment in .proto file.""" @staticmethod def SayHello(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/Greeter/SayHello', hello__pb2.HelloRequest.SerializeToString, hello__pb2.HelloResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
在这个文件中需要修改导入hello_pb2的方式,按照自己本地生成的路径进行导入。
在上面准备工作做完后可以进行server端和client端的开发:
- server.py
from concurrent import futures from rpc.grpc_demo01.proto import hello_pb2, hello_pb2_grpc import grpc class Greeter(hello_pb2_grpc.GreeterServicer): def SayHello(self, request, context): return hello_pb2.HelloResponse(message=f"Hello {request.name}") def server(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) hello_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port('[::]:8000') server.start() server.wait_for_termination() if __name__ == '__main__': server()
- client.py
import grpc from rpc.grpc_demo01.proto import hello_pb2, hello_pb2_grpc if __name__ == '__main__': with grpc.insecure_channel("127.0.0.1:8000") as channel: stub = hello_pb2_grpc.GreeterStub(channel) hello_request = hello_pb2.HelloRequest() hello_request.name = "bily" hello_response = stub.SayHello(hello_request) print(hello_response.message)
目录结构:
rpc │ __init__.py │ ├─go_server │ go_http_client.py │ go_json_client.py │ ├─grpc_demo01 │ │ client.py │ │ server.py │ │ __init__.py │ │ │ ├─proto │ │ │ hello.proto │ │ │ hello_pb2.py │ │ │ hello_pb2_grpc.py │ │ │ __init__.py │ │ │ │ │ └─__pycache__ │ │ hello_pb2.cpython-38.pyc │ │ hello_pb2_grpc.cpython-38.pyc │ │ __init__.cpython-38.pyc │ │ │ └─__pycache__ │ __init__.cpython-38.pyc
二、Go下grpc的开发
在开发之前需要先进行安装一些依赖:
- protoc工具 将proto文件转成go源码的工具
依赖包:
go get github.com/golang/protobuf/protoc-gen-go
go get google.golang.org/grpc
1、hello.proto
syntax = "proto3"; option go_package = "common/v1"; service Greeter { rpc SayHello(HelloRequest) returns (HelloResponse); } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; }
生成go源码:
protoc -I . hello.proto --go_out=plugins=grpc:.
2、hello.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 // protoc v3.19.4 // source: hello.proto package v1 import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type HelloRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *HelloRequest) Reset() { *x = HelloRequest{} if protoimpl.UnsafeEnabled { mi := &file_hello_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. func (*HelloRequest) Descriptor() ([]byte, []int) { return file_hello_proto_rawDescGZIP(), []int{0} } func (x *HelloRequest) GetName() string { if x != nil { return x.Name } return "" } type HelloResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` } func (x *HelloResponse) Reset() { *x = HelloResponse{} if protoimpl.UnsafeEnabled { mi := &file_hello_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloResponse) ProtoMessage() {} func (x *HelloResponse) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. func (*HelloResponse) Descriptor() ([]byte, []int) { return file_hello_proto_rawDescGZIP(), []int{1} } func (x *HelloResponse) GetMessage() string { if x != nil { return x.Message } return "" } var File_hello_proto protoreflect.FileDescriptor var file_hello_proto_rawDesc = []byte{ 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x34, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_hello_proto_rawDescOnce sync.Once file_hello_proto_rawDescData = file_hello_proto_rawDesc ) func file_hello_proto_rawDescGZIP() []byte { file_hello_proto_rawDescOnce.Do(func() { file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) }) return file_hello_proto_rawDescData } var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_hello_proto_goTypes = []interface{}{ (*HelloRequest)(nil), // 0: HelloRequest (*HelloResponse)(nil), // 1: HelloResponse } var file_hello_proto_depIdxs = []int32{ 0, // 0: Greeter.SayHello:input_type -> HelloRequest 1, // 1: Greeter.SayHello:output_type -> HelloResponse 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_hello_proto_init() } func file_hello_proto_init() { if File_hello_proto != nil { return } if !protoimpl.UnsafeEnabled { file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_hello_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_hello_proto_goTypes, DependencyIndexes: file_hello_proto_depIdxs, MessageInfos: file_hello_proto_msgTypes, }.Build() File_hello_proto = out.File file_hello_proto_rawDesc = nil file_hello_proto_goTypes = nil file_hello_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConnInterface // 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.SupportPackageIsVersion6 // GreeterClient is the client API for Greeter service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type GreeterClient interface { SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) } type greeterClient struct { cc grpc.ClientConnInterface } func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { return &greeterClient{cc} } func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { out := new(HelloResponse) err := c.cc.Invoke(ctx, "/Greeter/SayHello", in, out, opts...) if err != nil { return nil, err } return out, nil } // GreeterServer is the server API for Greeter service. type GreeterServer interface { SayHello(context.Context, *HelloRequest) (*HelloResponse, error) } // UnimplementedGreeterServer can be embedded to have forward compatible implementations. type UnimplementedGreeterServer struct { } func (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } 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: "/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: "Greeter", HandlerType: (*GreeterServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "SayHello", Handler: _Greeter_SayHello_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "hello.proto", }
3、server.go
package main import ( "context" "go_rpc_project/grpc_demo01/proto/common/v1" "google.golang.org/grpc" "log" "net" ) type Server struct { } func (s *Server) SayHello(ctx context.Context, request *v1.HelloRequest) (*v1.HelloResponse, error) { return &v1.HelloResponse{ Message: "Hello" + request.Name, }, nil } func main() { g := grpc.NewServer() v1.RegisterGreeterServer(g, &Server{}) lis, err := net.Listen("tcp", "0.0.0.0:8000") if err != nil { log.Fatal("listen failture", err.Error()) } err = g.Serve(lis) if err != nil { log.Fatal("start grpc failture", err.Error()) } }
4、client.go
package main import ( "context" "fmt" v1 "go_rpc_project/grpc_demo01/proto/common/v1" "google.golang.org/grpc" "log" ) func main() { conn, err := grpc.Dial("127.0.0.1:8000",grpc.WithInsecure()) if err != nil { log.Fatal("建立连接错误", err.Error()) } defer conn.Close() client := v1.NewGreeterClient(conn) response, err := client.SayHello(context.Background(), &v1.HelloRequest{Name: "bili"}) if err != nil { log.Fatal("调用方法错误", err.Error()) } fmt.Println(response.Message) }
5、目录结构
│─go_rpc_project
│ ├─grpc_demo01
│ │ ├─client
│ │ │ client.go
│ │ │
│ │ ├─proto
│ │ │ │ hello.proto
│ │ │ │
│ │ │ └─common
│ │ │ └─v1
│ │ │ hello.pb.go
│ │ │
│ │ └─server
│ │ server.go
三、grpc的四种数据流
RPC下的的服务端和客户端数据交互有四种模式:
- 简单模式(Simple RPC)
- 服务端数据流模式(Service-side streaming RPC)
- 客户端数据流模式(Client-side streaming RPC)
- 双向数据流模式(Bidirecitonal streaming RPC)
对于简单模式就是上面所说的传统的模式,客户端发送一次请求,服务端进行一次响应;服务端数据流就是客户端发送一次请求,服务端可以源源不断的返回一段联系的数据流,比如客户端实时不断的从服务端获取股票数据,其余两种与之类似。
1、stream.proto
syntax = "proto3"; option go_package = "common/v1"; // 声明grpc服务 service Greeter { /* 服务端推送流 客户端推送流 双向流 */ rpc GetStream(StreamReqData) returns (stream StreamResData); rpc PutStream(stream StreamReqData) returns (stream StreamResData); rpc AllStream(stream StreamReqData) returns (stream StreamResData); } // 请求stream结构 message StreamReqData { string data = 1; } // 响应stream结构 message StreamResData { string data = 1; }
执行下面的命令生成对应的源码:
protoc -I . stream.proto --go_out=plugins=grpc:.
2、stream.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 // protoc v3.19.4 // source: stream.proto package v1 import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // 请求stream结构 type StreamReqData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` } func (x *StreamReqData) Reset() { *x = StreamReqData{} if protoimpl.UnsafeEnabled { mi := &file_stream_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StreamReqData) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamReqData) ProtoMessage() {} func (x *StreamReqData) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamReqData.ProtoReflect.Descriptor instead. func (*StreamReqData) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{0} } func (x *StreamReqData) GetData() string { if x != nil { return x.Data } return "" } // 响应stream结构 type StreamResData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` } func (x *StreamResData) Reset() { *x = StreamResData{} if protoimpl.UnsafeEnabled { mi := &file_stream_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StreamResData) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamResData) ProtoMessage() {} func (x *StreamResData) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamResData.ProtoReflect.Descriptor instead. func (*StreamResData) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{1} } func (x *StreamResData) GetData() string { if x != nil { return x.Data } return "" } var File_stream_proto protoreflect.FileDescriptor var file_stream_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x23, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x9a, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x30, 0x01, 0x12, 0x2f, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2f, 0x0a, 0x09, 0x41, 0x6c, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0e, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_stream_proto_rawDescOnce sync.Once file_stream_proto_rawDescData = file_stream_proto_rawDesc ) func file_stream_proto_rawDescGZIP() []byte { file_stream_proto_rawDescOnce.Do(func() { file_stream_proto_rawDescData = protoimpl.X.CompressGZIP(file_stream_proto_rawDescData) }) return file_stream_proto_rawDescData } var file_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_stream_proto_goTypes = []interface{}{ (*StreamReqData)(nil), // 0: StreamReqData (*StreamResData)(nil), // 1: StreamResData } var file_stream_proto_depIdxs = []int32{ 0, // 0: Greeter.GetStream:input_type -> StreamReqData 0, // 1: Greeter.PutStream:input_type -> StreamReqData 0, // 2: Greeter.AllStream:input_type -> StreamReqData 1, // 3: Greeter.GetStream:output_type -> StreamResData 1, // 4: Greeter.PutStream:output_type -> StreamResData 1, // 5: Greeter.AllStream:output_type -> StreamResData 3, // [3:6] is the sub-list for method output_type 0, // [0:3] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_stream_proto_init() } func file_stream_proto_init() { if File_stream_proto != nil { return } if !protoimpl.UnsafeEnabled { file_stream_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StreamReqData); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_stream_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StreamResData); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_stream_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_stream_proto_goTypes, DependencyIndexes: file_stream_proto_depIdxs, MessageInfos: file_stream_proto_msgTypes, }.Build() File_stream_proto = out.File file_stream_proto_rawDesc = nil file_stream_proto_goTypes = nil file_stream_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConnInterface // 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.SupportPackageIsVersion6 // GreeterClient is the client API for Greeter service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type GreeterClient interface { // //服务端推送流 //客户端推送流 //双向流 GetStream(ctx context.Context, in *StreamReqData, opts ...grpc.CallOption) (Greeter_GetStreamClient, error) PutStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_PutStreamClient, error) AllStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_AllStreamClient, error) } type greeterClient struct { cc grpc.ClientConnInterface } func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { return &greeterClient{cc} } func (c *greeterClient) GetStream(ctx context.Context, in *StreamReqData, opts ...grpc.CallOption) (Greeter_GetStreamClient, error) { stream, err := c.cc.NewStream(ctx, &_Greeter_serviceDesc.Streams[0], "/Greeter/GetStream", opts...) if err != nil { return nil, err } x := &greeterGetStreamClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } type Greeter_GetStreamClient interface { Recv() (*StreamResData, error) grpc.ClientStream } type greeterGetStreamClient struct { grpc.ClientStream } func (x *greeterGetStreamClient) Recv() (*StreamResData, error) { m := new(StreamResData) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *greeterClient) PutStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_PutStreamClient, error) { stream, err := c.cc.NewStream(ctx, &_Greeter_serviceDesc.Streams[1], "/Greeter/PutStream", opts...) if err != nil { return nil, err } x := &greeterPutStreamClient{stream} return x, nil } type Greeter_PutStreamClient interface { Send(*StreamReqData) error Recv() (*StreamResData, error) grpc.ClientStream } type greeterPutStreamClient struct { grpc.ClientStream } func (x *greeterPutStreamClient) Send(m *StreamReqData) error { return x.ClientStream.SendMsg(m) } func (x *greeterPutStreamClient) Recv() (*StreamResData, error) { m := new(StreamResData) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *greeterClient) AllStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_AllStreamClient, error) { stream, err := c.cc.NewStream(ctx, &_Greeter_serviceDesc.Streams[2], "/Greeter/AllStream", opts...) if err != nil { return nil, err } x := &greeterAllStreamClient{stream} return x, nil } type Greeter_AllStreamClient interface { Send(*StreamReqData) error Recv() (*StreamResData, error) grpc.ClientStream } type greeterAllStreamClient struct { grpc.ClientStream } func (x *greeterAllStreamClient) Send(m *StreamReqData) error { return x.ClientStream.SendMsg(m) } func (x *greeterAllStreamClient) Recv() (*StreamResData, error) { m := new(StreamResData) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } // GreeterServer is the server API for Greeter service. type GreeterServer interface { // //服务端推送流 //客户端推送流 //双向流 GetStream(*StreamReqData, Greeter_GetStreamServer) error PutStream(Greeter_PutStreamServer) error AllStream(Greeter_AllStreamServer) error } // UnimplementedGreeterServer can be embedded to have forward compatible implementations. type UnimplementedGreeterServer struct { } func (*UnimplementedGreeterServer) GetStream(*StreamReqData, Greeter_GetStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetStream not implemented") } func (*UnimplementedGreeterServer) PutStream(Greeter_PutStreamServer) error { return status.Errorf(codes.Unimplemented, "method PutStream not implemented") } func (*UnimplementedGreeterServer) AllStream(Greeter_AllStreamServer) error { return status.Errorf(codes.Unimplemented, "method AllStream not implemented") } func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { s.RegisterService(&_Greeter_serviceDesc, srv) } func _Greeter_GetStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(StreamReqData) if err := stream.RecvMsg(m); err != nil { return err } return srv.(GreeterServer).GetStream(m, &greeterGetStreamServer{stream}) } type Greeter_GetStreamServer interface { Send(*StreamResData) error grpc.ServerStream } type greeterGetStreamServer struct { grpc.ServerStream } func (x *greeterGetStreamServer) Send(m *StreamResData) error { return x.ServerStream.SendMsg(m) } func _Greeter_PutStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).PutStream(&greeterPutStreamServer{stream}) } type Greeter_PutStreamServer interface { Send(*StreamResData) error Recv() (*StreamReqData, error) grpc.ServerStream } type greeterPutStreamServer struct { grpc.ServerStream } func (x *greeterPutStreamServer) Send(m *StreamResData) error { return x.ServerStream.SendMsg(m) } func (x *greeterPutStreamServer) Recv() (*StreamReqData, error) { m := new(StreamReqData) if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func _Greeter_AllStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).AllStream(&greeterAllStreamServer{stream}) } type Greeter_AllStreamServer interface { Send(*StreamResData) error Recv() (*StreamReqData, error) grpc.ServerStream } type greeterAllStreamServer struct { grpc.ServerStream } func (x *greeterAllStreamServer) Send(m *StreamResData) error { return x.ServerStream.SendMsg(m) } func (x *greeterAllStreamServer) Recv() (*StreamReqData, error) { m := new(StreamReqData) if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } return m, nil } var _Greeter_serviceDesc = grpc.ServiceDesc{ ServiceName: "Greeter", HandlerType: (*GreeterServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "GetStream", Handler: _Greeter_GetStream_Handler, ServerStreams: true, }, { StreamName: "PutStream", Handler: _Greeter_PutStream_Handler, ServerStreams: true, ClientStreams: true, }, { StreamName: "AllStream", Handler: _Greeter_AllStream_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "stream.proto", }
3、server.go
package main import ( "fmt" v1 "go_rpc_project/grpc_stream_demo/proto/common/v1" "google.golang.org/grpc" "log" "net" "sync" "time" ) type Server struct { } // 服务端单向流 func (s *Server) GetStream(req *v1.StreamReqData, res v1.Greeter_GetStreamServer) error { i := 0 for { i++ _ = res.Send(&v1.StreamResData{Data: fmt.Sprintf("v%", time.Now().Unix())}) time.Sleep(time.Second) if i > 20 { break } } return nil } // 客户端单向流 func (s *Server) PutStream(cliStr v1.Greeter_PutStreamServer) error { for { if tem, err := cliStr.Recv(); err == nil { log.Println(tem) } else { log.Println("break, err :", err) break } } return nil } // 双向流 func (s *Server) AllStream(allStr v1.Greeter_AllStreamServer) error { wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() for { data, _ := allStr.Recv() fmt.Println(data) } }() go func() { defer wg.Done() for { _ = allStr.Send(&v1.StreamResData{Data: "ok!"}) } }() wg.Wait() return nil } func main() { // 创建连接 lis, err := net.Listen("tcp", ":8000") if err != nil { panic(err) return } // 创建一个grpc服务器 g := grpc.NewServer() // 注册服务 v1.RegisterGreeterServer(g, &Server{}) // 处理连接 err = g.Serve(lis) if err != nil { panic(err) return } }
4、client.go
package main import ( "context" "fmt" v1 "go_rpc_project/grpc_stream_demo/proto/common/v1" "google.golang.org/grpc" "log" "time" ) func main() { // 通过grpc建立连接 conn, err := grpc.Dial("127.0.0.1:8000", grpc.WithInsecure()) if err != nil { log.Fatal("建立连接错误", err) return } defer conn.Close() // 通过连接生成client client := v1.NewGreeterClient(conn) //调用服务器推送流 reqStreamData := &v1.StreamReqData{Data: "我是客户端数据!"} res, _ := client.GetStream(context.Background(), reqStreamData) for { // 接收服务端发送的数据 data, err := res.Recv() if err != nil { break } fmt.Println(data) } // 客户端推送流 putRes, _ := client.PutStream(context.Background()) i := 1 for { i++ _ = putRes.Send(&v1.StreamReqData{Data: "客户端推送流数据"}) time.Sleep(time.Second) if i > 20 { break } } // 双向流数据 allRes, _ := client.AllStream(context.Background()) // 接收数据 go func() { for { data, _ := allRes.Recv() fmt.Println(data) } }() // 发送数据 go func() { for { _ = allRes.Send(&v1.StreamReqData{Data: "双向流客户端数据!"}) time.Sleep(time.Second) } }() select {} }
5、目录结构
├─go_rpc_project
│├─grpc_stream_demo
││ ├─client
││ │ client.go
││ │
││ ├─proto
││ │ │ stream.proto
││ │ │
││ │ └─common
││ │ └─v1
││ │ stream.pb.go
││ │
││ └─server
││ server.go