• 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)
    View Code
    • 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)
    View Code

    在这个文件中需要修改导入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",
    }
    View Code

    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",
    }
    View Code

    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
  • 相关阅读:
    Contract
    VS.NET 发布 常识
    案例分析:培训合同与赔款事宜
    DNN最新资源,目前在学SAP,所以.Net的工作就停滞了。
    劳动争议处理依据劳动法
    开发网格应用程序<1>
    BI在中国成了装饰品
    VOIP侵蚀电信利益,未来最赚钱的行业.
    [转]个人知识管理-Web2.0技术下的一个热点
    [转]冲杯三鹿奶粉给你喝!
  • 原文地址:https://www.cnblogs.com/shenjianping/p/15865468.html
Copyright © 2020-2023  润新知