• Golang的序列化-RPC和GRPC


              Golang的序列化-RPC和GRPC

                                   作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

     

     

     

    一.RPC概述

    1>.什么是RPC

      RPC(Remote Procedure Call Protocol),是远程过程调用的缩写,通俗的说就是调用远处(一般指不同的主机)的一个函数。

    2>.为什么微服务需要RPC

      我们使用微服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现公司跨团队的技术解耦。
    
      这样的话,如果没有统一的服务框架,RPC框架,各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动,造成整体的低效。

      所以,统一RPC框架把上述“业务之外”的技术劳动统一处理,是服务化首要解决的问题。

    二.RPC入门案例

      在互联网时代,RPC已经和IPC(进程间通信)一样成为一个不可或缺的基础构件。因此Go语言的标准库也提供了一个简单的RPC实现,我们将以此为入口学习RPC的常见用法。

    1>.RPC的服务端

    package main
    
    import (
        "fmt"
        "net"
        "net/rpc"
    )
    
    type Zabbix struct{}
    
    /**
    定义成员方法:
        第一个参数是传入参数.
        第二个参数必须是传出参数(引用类型).
    
    Go语言的RPC规则:
        方法只能有两个可序列化的参数,其中第二个参数是指针类型,并且返回一个error类型,同时必须是公开的方法。
    
    温馨提示:
        当调用远程函数之后,如果返回的错误不为空,那么传出参数为空。
    */
    func (Zabbix) MonitorHosts(name string, response *string) error {
        *response = name + "主机监控中..."
        return nil
    }
    
    func main() {
        /**
        进程间交互有很多种,比如基于信号,共享内存,管道,套接字等方式.
    
        1>.rpc基于是TCP的,因此我们需要先开启监听端口
        */
        listener, err := net.Listen("tcp", ":8888")
        if err != nil {
            fmt.Println("开启监听器失败,错误原因: ", err)
            return
        }
        defer listener.Close()
        fmt.Println("服务启动成功...")
    
        /**
        2>.接受链接,即接受传输的数据
        */
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("建立链接失败...")
            return
        }
        defer conn.Close()
        fmt.Println("建立连接: ", conn.RemoteAddr())
        /**
        3>.注册rpc服务,维护一个hash表,key值是服务名称,value值是服务的地址。服务器有很多函数,希望被调用的函数需要注册到RPC上。
        以下是RegisterName的函数签名:
            func RegisterName(name string, rcvr interface{}) error
        以下是对函数签名相关参数的说明:
            name:
                指的是服务名称。
            rcvr:
                指的是结构体对象(这个结构体必须含有成员方法)。
        */
        rpc.RegisterName("zabbix", new(Zabbix))
    
        /**
        4>.链接的处理交给RCP框架处理,即rpc调用,并返回执行后的数据,其工作原理大致分为以下3个步骤:
            (1)read,获取服务名称和方法名,获取请求数据;
            (2)调用对应服务里面的方法,获取传出数据;
            (3)write,把数据返回给client;
        */
        rpc.ServeConn(conn)
    }

    2>.RPC的客户端

    package main
    
    import (
        "fmt"
        "net"
        "net/rpc"
    )
    
    func main() {
        /**
        1>.首选是通过rpc.Dial拨号RPC服务
    
        温馨提示:
            默认数据传输过程中编码方式是gob,可以选择json
        */
        conn, err := net.Dial("tcp", "127.0.0.1:8888")
        if err != nil {
            fmt.Println("链接服务器失败")
            return
        }
        defer conn.Close()
    
        /**
        2>.把conn和rpc进行绑定
        */
        client := rpc.NewClient(conn)
    
        /**
        3>.然后通过client.Call调用具体的RPC方法。其中Call函数的签名如下所示:
                func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error
            以下是对函数签名的相关参数进行补充说明:
                serviceMethod:
                    用点号(.)链接的RPC服务名字和方法名字
                args:
                    指定输入参数
                reply:
                    指定输出参数接收的
        */
        var data string
        err = client.Call("zabbix.MonitorHosts", "Nginx", &data)
        if err != nil {
            fmt.Println("远程接口调用失败,错误原因: ", err)
            return
        }
        fmt.Println(data)
    }

    三.跨语言的RPC

      标准库的RPC默认采用Go语言特有的gob编码,因此从其它语言调用Go语言实现的RPC服务将比较困难。跨语言是互联网时代RPC的一个首要条件,这里我们再来实现一个跨语言的RPC。

      得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言支持的。这里我们将尝试通过官方自带的net
    /rpc/jsonrpc扩展实现一个跨语言RPC。

    1>.RPC的服务端

    package main
    
    import (
        "fmt"
        "net"
        "net/rpc"
        "net/rpc/jsonrpc"
    )
    
    type OpenFalcon struct{}
    
    /**
    定义成员方法:
        第一个参数是传入参数.
        第二个参数必须是传出参数(引用类型).
    
    Go语言的RPC规则:
        方法只能有两个可序列化的参数,其中第二个参数是指针类型,并且返回一个error类型,同时必须是公开的方法。
    
    温馨提示:
        当调用远程函数之后,如果返回的错误不为空,那么传出参数为空。
    */
    func (OpenFalcon) MonitorHosts(name string, response *string) error {
        *response = name + "主机监控中..."
        return nil
    }
    
    func main() {
        /**
        进程间交互有很多种,比如基于信号,共享内存,管道,套接字等方式.
    
        1>.rpc基于是TCP的,因此我们需要先开启监听端口
        */
        listener, err := net.Listen("tcp", ":8888")
        if err != nil {
            fmt.Println("开启监听器失败,错误原因: ", err)
            return
        }
        defer listener.Close()
        fmt.Println("服务启动成功...")
    
        /**
        2>.接受链接,即接受传输的数据
        */
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("建立链接失败...")
            return
        }
        defer conn.Close()
        fmt.Println("建立连接: ", conn.RemoteAddr())
        /**
        3>.注册rpc服务,维护一个hash表,key值是服务名称,value值是服务的地址。服务器有很多函数,希望被调用的函数需要注册到RPC上。
        以下是RegisterName的函数签名:
            func RegisterName(name string, rcvr interface{}) error
        以下是对函数签名相关参数的说明:
            name:
                指的是服务名称。
            rcvr:
                指的是结构体对象(这个结构体必须含有成员方法)。
        */
        rpc.RegisterName("open_falcon", new(OpenFalcon))
    
        /**
        4>.链接的处理交给RCP框架处理,即rpc调用,并返回执行后的数据,其工作原理大致分为以下3个步骤:
            (1)read,获取服务名称和方法名,获取请求数据;
            (2)调用对应服务里面的方法,获取传出数据;
            (3)write,把数据返回给client;
        */
        jsonrpc.ServeConn(conn)
    }

    2>.RPC的客户端

    package main
    
    import (
        "fmt"
        "net/rpc/jsonrpc"
    )
    
    func main() {
        /**
        首选是通过rpc.Dial拨号RPC服务
    
        温馨提示:
            默认数据传输过程中编码方式是gob,可以选择json,需要导入"net/rpc/jsonrpc"包。
        */
        conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8888")
        if err != nil {
            fmt.Println("链接服务器失败")
            return
        }
        defer conn.Close()
    
        var data string
    
        /**
        其中Call函数的签名如下所示:
            func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error
        以下是对函数签名的相关参数进行补充说明:
            serviceMethod:
                用点号(.)链接的RPC服务名字和方法名字
            args:
                指定输入参数
            reply:
                指定输出参数接收的
        */
        err = conn.Call("open_falcon.MonitorHosts", "Httpd", &data)
        if err != nil {
            fmt.Println("远程接口调用失败,错误原因: ", err)
            return
        }
        fmt.Println(data)
    }

    四.ProtoBuf

      博主推荐阅读:
        https://www.cnblogs.com/yinzhengjie2020/p/12741943.html

    五.GRPC框架

    1>.什么是GRPC

      GRPC是Google公司基于Protobuf开发的跨语言的开源RPC框架。
    
      GRPC是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供C,Java和Go语言版本,分别是:grpc, grpc-java, grpc-go. 其中C版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
    
      GRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
    
      博主推荐阅读:
        GRPC 官方文档中文版:
          http://doc.oschina.net/grpc?t=60133
        GRPC官网:
          https://grpc.io

    2>.安装grpc环境

    C:Usersyinzhengjie>go get -u -v google.golang.org/grpc

     

    3>.基于protobuf编写Grpc服务

    ////protobuf默认支持的版本是2.0,现在一般使用3.0版本,所以需要手动指定版本号
    //c语言的编程风格
    syntax = "proto3";
    
    //指定包名
    package pb;
    
    //定义传输数据的格式
    message People{
        string name = 1; //1表示表示字段是1   数据库中表的主键id等于1,主键不能重复,标示位数据不能重复
        //标示位不能使用19000 -19999  系统预留位
        int32 age = 2;
    
        //结构体嵌套
        student s = 3;
        //使用数组/切片
        repeated string phone = 4;
    
        //oneof的作用是多选一
        oneof data{
            int32 score = 5;
            string city = 6;
            bool good = 7;
        }
    }
    
    //oneof  c语言中的联合体
    
    message student{
        string name = 1;
        int32 age = 6;
    }
    
    //通过定义服务,然后借助框架,帮助实现部分的rpc代码
    service Hello{
        rpc World(student)returns(student);
    }
    grpc.proto
    ////protobuf默认支持的版本是2.0,现在一般使用3.0版本,所以需要手动指定版本号
    //c语言的编程风格
    
    // Code generated by protoc-gen-go. DO NOT EDIT.
    // versions:
    //     protoc-gen-go v1.21.0
    //     protoc        v3.11.4
    // source: grpc.proto
    
    //指定包名
    
    package pb
    
    import (
        context "context"
        proto "github.com/golang/protobuf/proto"
        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)
    )
    
    // This is a compile-time assertion that a sufficiently up-to-date version
    // of the legacy proto package is being used.
    const _ = proto.ProtoPackageIsVersion4
    
    //定义传输数据的格式
    type People struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
    
        Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` //1表示表示字段是1   数据库中表的主键id等于1,主键不能重复,标示位数据不能重复
        //标示位不能使用19000 -19999  系统预留位
        Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
        //结构体嵌套
        S *Student `protobuf:"bytes,3,opt,name=s,proto3" json:"s,omitempty"`
        //使用数组/切片
        Phone []string `protobuf:"bytes,4,rep,name=phone,proto3" json:"phone,omitempty"`
        //oneof的作用是多选一
        //
        // Types that are assignable to Data:
        //    *People_Score
        //    *People_City
        //    *People_Good
        Data isPeople_Data `protobuf_oneof:"data"`
    }
    
    func (x *People) Reset() {
        *x = People{}
        if protoimpl.UnsafeEnabled {
            mi := &file_grpc_proto_msgTypes[0]
            ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
            ms.StoreMessageInfo(mi)
        }
    }
    
    func (x *People) String() string {
        return protoimpl.X.MessageStringOf(x)
    }
    
    func (*People) ProtoMessage() {}
    
    func (x *People) ProtoReflect() protoreflect.Message {
        mi := &file_grpc_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 People.ProtoReflect.Descriptor instead.
    func (*People) Descriptor() ([]byte, []int) {
        return file_grpc_proto_rawDescGZIP(), []int{0}
    }
    
    func (x *People) GetName() string {
        if x != nil {
            return x.Name
        }
        return ""
    }
    
    func (x *People) GetAge() int32 {
        if x != nil {
            return x.Age
        }
        return 0
    }
    
    func (x *People) GetS() *Student {
        if x != nil {
            return x.S
        }
        return nil
    }
    
    func (x *People) GetPhone() []string {
        if x != nil {
            return x.Phone
        }
        return nil
    }
    
    func (m *People) GetData() isPeople_Data {
        if m != nil {
            return m.Data
        }
        return nil
    }
    
    func (x *People) GetScore() int32 {
        if x, ok := x.GetData().(*People_Score); ok {
            return x.Score
        }
        return 0
    }
    
    func (x *People) GetCity() string {
        if x, ok := x.GetData().(*People_City); ok {
            return x.City
        }
        return ""
    }
    
    func (x *People) GetGood() bool {
        if x, ok := x.GetData().(*People_Good); ok {
            return x.Good
        }
        return false
    }
    
    type isPeople_Data interface {
        isPeople_Data()
    }
    
    type People_Score struct {
        Score int32 `protobuf:"varint,5,opt,name=score,proto3,oneof"`
    }
    
    type People_City struct {
        City string `protobuf:"bytes,6,opt,name=city,proto3,oneof"`
    }
    
    type People_Good struct {
        Good bool `protobuf:"varint,7,opt,name=good,proto3,oneof"`
    }
    
    func (*People_Score) isPeople_Data() {}
    
    func (*People_City) isPeople_Data() {}
    
    func (*People_Good) isPeople_Data() {}
    
    type Student struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
    
        Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
        Age  int32  `protobuf:"varint,6,opt,name=age,proto3" json:"age,omitempty"`
    }
    
    func (x *Student) Reset() {
        *x = Student{}
        if protoimpl.UnsafeEnabled {
            mi := &file_grpc_proto_msgTypes[1]
            ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
            ms.StoreMessageInfo(mi)
        }
    }
    
    func (x *Student) String() string {
        return protoimpl.X.MessageStringOf(x)
    }
    
    func (*Student) ProtoMessage() {}
    
    func (x *Student) ProtoReflect() protoreflect.Message {
        mi := &file_grpc_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 Student.ProtoReflect.Descriptor instead.
    func (*Student) Descriptor() ([]byte, []int) {
        return file_grpc_proto_rawDescGZIP(), []int{1}
    }
    
    func (x *Student) GetName() string {
        if x != nil {
            return x.Name
        }
        return ""
    }
    
    func (x *Student) GetAge() int32 {
        if x != nil {
            return x.Age
        }
        return 0
    }
    
    var File_grpc_proto protoreflect.FileDescriptor
    
    var file_grpc_proto_rawDesc = []byte{
        0x0a, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62,
        0x22, 0xab, 0x01, 0x0a, 0x06, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
        0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
        0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67,
        0x65, 0x12, 0x19, 0x0a, 0x01, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70,
        0x62, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x01, 0x73, 0x12, 0x14, 0x0a, 0x05,
        0x70, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x6f,
        0x6e, 0x65, 0x12, 0x16, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
        0x05, 0x48, 0x00, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x04, 0x63, 0x69,
        0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79,
        0x12, 0x14, 0x0a, 0x04, 0x67, 0x6f, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00,
        0x52, 0x04, 0x67, 0x6f, 0x6f, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2f,
        0x0a, 0x07, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
        0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a,
        0x03, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x32,
        0x2a, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x21, 0x0a, 0x05, 0x57, 0x6f, 0x72, 0x6c,
        0x64, 0x12, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x1a, 0x0b,
        0x2e, 0x70, 0x62, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f,
        0x74, 0x6f, 0x33,
    }
    
    var (
        file_grpc_proto_rawDescOnce sync.Once
        file_grpc_proto_rawDescData = file_grpc_proto_rawDesc
    )
    
    func file_grpc_proto_rawDescGZIP() []byte {
        file_grpc_proto_rawDescOnce.Do(func() {
            file_grpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpc_proto_rawDescData)
        })
        return file_grpc_proto_rawDescData
    }
    
    var file_grpc_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
    var file_grpc_proto_goTypes = []interface{}{
        (*People)(nil),  // 0: pb.People
        (*Student)(nil), // 1: pb.student
    }
    var file_grpc_proto_depIdxs = []int32{
        1, // 0: pb.People.s:type_name -> pb.student
        1, // 1: pb.Hello.World:input_type -> pb.student
        1, // 2: pb.Hello.World:output_type -> pb.student
        2, // [2:3] is the sub-list for method output_type
        1, // [1:2] is the sub-list for method input_type
        1, // [1:1] is the sub-list for extension type_name
        1, // [1:1] is the sub-list for extension extendee
        0, // [0:1] is the sub-list for field type_name
    }
    
    func init() { file_grpc_proto_init() }
    func file_grpc_proto_init() {
        if File_grpc_proto != nil {
            return
        }
        if !protoimpl.UnsafeEnabled {
            file_grpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
                switch v := v.(*People); i {
                case 0:
                    return &v.state
                case 1:
                    return &v.sizeCache
                case 2:
                    return &v.unknownFields
                default:
                    return nil
                }
            }
            file_grpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
                switch v := v.(*Student); i {
                case 0:
                    return &v.state
                case 1:
                    return &v.sizeCache
                case 2:
                    return &v.unknownFields
                default:
                    return nil
                }
            }
        }
        file_grpc_proto_msgTypes[0].OneofWrappers = []interface{}{
            (*People_Score)(nil),
            (*People_City)(nil),
            (*People_Good)(nil),
        }
        type x struct{}
        out := protoimpl.TypeBuilder{
            File: protoimpl.DescBuilder{
                GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
                RawDescriptor: file_grpc_proto_rawDesc,
                NumEnums:      0,
                NumMessages:   2,
                NumExtensions: 0,
                NumServices:   1,
            },
            GoTypes:           file_grpc_proto_goTypes,
            DependencyIndexes: file_grpc_proto_depIdxs,
            MessageInfos:      file_grpc_proto_msgTypes,
        }.Build()
        File_grpc_proto = out.File
        file_grpc_proto_rawDesc = nil
        file_grpc_proto_goTypes = nil
        file_grpc_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
    
    // HelloClient is the client API for Hello service.
    //
    // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
    type HelloClient interface {
        World(ctx context.Context, in *Student, opts ...grpc.CallOption) (*Student, error)
    }
    
    type helloClient struct {
        cc grpc.ClientConnInterface
    }
    
    func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
        return &helloClient{cc}
    }
    
    func (c *helloClient) World(ctx context.Context, in *Student, opts ...grpc.CallOption) (*Student, error) {
        out := new(Student)
        err := c.cc.Invoke(ctx, "/pb.Hello/World", in, out, opts...)
        if err != nil {
            return nil, err
        }
        return out, nil
    }
    
    // HelloServer is the server API for Hello service.
    type HelloServer interface {
        World(context.Context, *Student) (*Student, error)
    }
    
    // UnimplementedHelloServer can be embedded to have forward compatible implementations.
    type UnimplementedHelloServer struct {
    }
    
    func (*UnimplementedHelloServer) World(context.Context, *Student) (*Student, error) {
        return nil, status.Errorf(codes.Unimplemented, "method World not implemented")
    }
    
    func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
        s.RegisterService(&_Hello_serviceDesc, srv)
    }
    
    func _Hello_World_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
        in := new(Student)
        if err := dec(in); err != nil {
            return nil, err
        }
        if interceptor == nil {
            return srv.(HelloServer).World(ctx, in)
        }
        info := &grpc.UnaryServerInfo{
            Server:     srv,
            FullMethod: "/pb.Hello/World",
        }
        handler := func(ctx context.Context, req interface{}) (interface{}, error) {
            return srv.(HelloServer).World(ctx, req.(*Student))
        }
        return interceptor(ctx, in, info, handler)
    }
    
    var _Hello_serviceDesc = grpc.ServiceDesc{
        ServiceName: "pb.Hello",
        HandlerType: (*HelloServer)(nil),
        Methods: []grpc.MethodDesc{
            {
                MethodName: "World",
                Handler:    _Hello_World_Handler,
            },
        },
        Streams:  []grpc.StreamDesc{},
        Metadata: "grpc.proto",
    }
    命令行执行"protoc --go_out=plugins=grpc:. grpc.proto "生成"grpc.pb.go"文件

    4>.grpcServer.go

    package main
    
    import (
        "context"
        "google.golang.org/grpc"
        "net"
        "yinzhengjie/pb"
    )
    
    
    //定义一个结构体,继承自HelloServer接口(该接口是我们通过protobuf代码生成的)
    type HelloService struct {}
    
    func (HelloService)World(ctx context.Context, req*pb.Student) (*pb.Student, error){
        req.Name += " nihao"
        req.Age += 10
        return req,nil
    }
    
    
    func main()  {
        //先获取grpc对象
        grpcServer := grpc.NewServer()
    
        //注册服务
        pb.RegisterHelloServer(grpcServer,new(HelloService))
    
        //开启监听
        lis,err := net.Listen("tcp",":8888")
        if err != nil {
            return
        }
        defer lis.Close()
    
        //先获取grpc服务端对象
        grpcServer.Serve(lis)
    }

    5>.grpcClient.go

    package main
    
    import (
        "google.golang.org/grpc"
        "context"
        "fmt"
        "yinzhengjie/pb"
    )
    
    func main()  {
    
        //和grpc服务端建立连接
        grpcCnn ,err := grpc.Dial("127.0.0.1:8888",grpc.WithInsecure())
        if err != nil {
            fmt.Println(err)
            return
        }
        defer grpcCnn.Close()
    
        //得到一个客户端对象
        client :=pb.NewHelloClient(grpcCnn)
    
        var s pb.Student
        s.Name = "Jason Yin"
        s.Age = 20
    
        resp,err := client.World(context.TODO(),&s)
        fmt.Println(resp,err)
    }
  • 相关阅读:
    Excel Add-in
    并发控制MsSql
    Kaggle实战分类问题2
    NuGet
    Pomelo分布式游戏服务器框架
    Ambari
    oracle 多行转多列查询
    Oauth2.0 用Spring-security-oauth2
    bug排查小结
    Linux之lsof命令
  • 原文地址:https://www.cnblogs.com/yinzhengjie2020/p/12757971.html
Copyright © 2020-2023  润新知