• RPC与Protobuf(三)


    Protobuf介绍

      Protobuf 是Protocol Buffers 的简称,它是谷歌公司开发的一种数据描述语言, 2008开源时定位类似于XML、JSON等描述语言,通过附带的工具生成代码并实现结构化数据的功能,但我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言RPC接口的基础工具。

    Protobuf入门

      对于没有用过Protobuf的,建议先从官网了解基本用法,下面我们结合Protobuf和RPC结合一起使用,通过Protobuf来最终保证RPC的接口规范和安全。Protobuf中最基本的数据单元是message,是类似于Go语言中结构体的存在,在message中可以嵌套message或其他的基础数据类型成员。

    简单实例

    文件目录

    ---rpc_demo
    │  client.go
    │  go.mod
    │  go.sum
    │  service.go
    │
    └─proto
            hello.pb.go
            hello.proto

    首先在项目目录下创建一个proto目录用于存放proto文件,创建简单的helllo.proto文件:

    syntax = "proto3";  // 声明语法
    
    package proto;  // 可根据自己当前包结构自定义
    
    message String {   // message 关键字定义了一个 String类型,且只有一个字符串类型的value成员,该成员编号为1来代替名字,这就是为什么protobuf体积小的原因,别的数据描述语言(json、xml)都是通过成员名字标识,而Portobuf通过唯一编号,不过不便于查阅
        string value = 1;
    }

    进入到proto文件目录下,使用以下命令生成相应的语言脚本:protoc  --go_out=. hello.proto 生成相应的接口文件。然后我们可以根据生成的go文件,写服务端和客户端代码,先写出服务端代码。

    注意:Protobuf核心代码是使用C++编写的,在官方的protobuf编译器中并不支持Go语言,要想基于hello.proto文件生成相应的go代码,我们需要下载相应的插件: go get github.com/golang/protobuf/protoc-gen-go 命令安装,最后通过protoc  --go_out=. hello.proto会生成相应的go文件代码

    package main
    
    import (
       "log"
       "net"
       "net/rpc"
       "rpc_protobuf/proto"
    )
    
    type HelloService struct {}
    
    // 实现rpc服务的具体方法,注意接受的参数要满足接口的要求标准,因为目前调用的都是结构体,所以采用指针的参数
    func (p *HelloService) Hello (request *proto.String, reply *proto.String) error {
       reply.Value = "hello: " + request.GetValue()
       return nil
    }
    
    func main() {
       // 注册rpc服务
       rpc.RegisterName("HelloService", new(HelloService))
       // 监听tcp服务
       listenr, err :=  net.Listen("tcp",":1234")
       if err != nil {
          log.Fatal("Listen error:",err)
       }
       log.Println("service starting....")
       // 有相关数据请求则接受,并将连挂在rpc服务上
       conn, err := listenr.Accept()
       if err != nil {
          log.Fatal("Accept error",err)
       }
    
       rpc.ServeConn(conn)
    }
    

      

    最后写出客户端代码:

    package main
    
    import (
       "fmt"
       "log"
       "net/rpc"
       "rpc_protobuf/proto"
    )
    
    func main() {
       // 拨号本端1234端口服务
       client, err := rpc.Dial("tcp", "localhost:1234")
       if err != nil {
          log.Fatal("TCP error:", err)
       }
    
       // 统一采用接口提供的参数,和请求参数
       var reply = &proto.String{}
       var param = &proto.String{
          Value: "Wang",
       }
    
       // 调用远程服务
       err = client.Call("HelloService.Hello", &param, &reply)
       if err != nil {
          log.Fatal(err)
       }
    
       fmt.Println(reply)
    }
    

      

    (注:在使用Goland IDE的使用,如果我们使用了go mod,在setting中的Go modules选项中的enable Go Moudle(vgo) integration 和 vendoring mode 需要调整当导包出现问题时)

    我们分别启动服务端、客户端就可以查看相应的结果了,这里我们简单的使用了rpc和protobuf,之后会介绍怎么下载protoc、protobuf等

    分析:

    下面我们分析下生成的hello.pb.go文件(截取部分):

    package proto  // 包名
    ....
    
    // 实现了一个字符类型,根据proto文件生成的
    type String struct {
    	Value                string   `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
    	XXX_NoUnkeyedLiteral struct{} `json:"-"`
    	XXX_unrecognized     []byte   `json:"-"`
    	XXX_sizecache        int32    `json:"-"`
    }
    
    ....
    // 用于给String类型取值的,在服务可以通过这个方法进行参数的获取,每个成员都会有Get方法
    func (m *String) GetValue() string {
    	if m != nil {
    		return m.Value
    	}
    	return ""
    }
    

      

    真正的价值

    上面我们已经讲解过通过复杂的操作来结合rpc和protobuf,我们使用protobuf去定义输入和输出参数以及实现的结构体,但是大家有没有发现,其实生成的hello.pb.go中是没有服务函数的只有一些接口约束,那我们能不能将RPC中的服务也定义在Protobuf中?这样我们就实现了用Protobuf定义语言无关的RPC服务接口,这才是真正的价值。

    这就属于Portobuf自定制的范围了,有兴趣可以阅读《Go语言高级编程》,之前的部分文章也是摘取于这本书中。

  • 相关阅读:
    node.js mongodb笔记
    查询数据库的连接情况
    使用cmd命令查看电脑端口占用情况
    正则表达式基本语法
    ASCII码对照表
    使用命令实现IIS 站点和应用程序池自动启动和停止
    postgresql常见命令及操作
    apigateway-kong(四)负载均衡理论及实现
    apigateway-kong(三)Proxy规则
    apigateway-kong(二)admin-api(结合实例比官网还详细)
  • 原文地址:https://www.cnblogs.com/double-W/p/12742411.html
Copyright © 2020-2023  润新知