Fork me on GitHub

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

 

posted @ 2022-02-08 22:22  iveBoy  阅读(330)  评论(0编辑  收藏  举报
TOP