美文网首页
Golang 加载 protobuffer 文件,动态解析 pb

Golang 加载 protobuffer 文件,动态解析 pb

作者: 不李不外的 | 来源:发表于2020-07-20 15:28 被阅读0次

    在业务中经常有这种需求,给某某服务加一个命令字,用来接收的RPC 请求来的protobuffer数据,按照某个pb数据定义反序列化之后,转成JSON 再传输到下游服务。

    如果安装我们原来的方案,可能需要每次都修改pb文件,再编译服务再上线

    旧的方式:
    1、修改proto文件。
    2、protoc 产出 *.pb.go文件,
    3、编译服务。

    但是实际上我们可以在golang 代码里面自动解析proto文件,整个流程在服务内部自动完成。

    新的方式:
    1、启动服务(从配置服务器加载 proto文件)
    2、通过proto文件产生的一个 FileDescriptor ,进而根据对象名称找到 MessageDescriptor
    3、直接用MessageDescriptor

    可以看到新的方式 ,本质就是需要实现动态pb解析和协议转换的工作。

    下面我们看示例代码
    保存如下pb定义为test.proto

    syntax = "proto2";
    package test;
    message AddFriendReq {
        repeated string phone = 1;
        optional string keyword =2;
    }
    

    示例代码
    代码本身是依赖了一个三方包:github.com/jhump/protoreflect

    package main
    
    import (
        "bytes"
        "fmt"
    
        testpb "github.com/lilien1010/my_gotest/proto" //这个包是从上面的pb文件生产的,用来做序列化测试
        "github.com/golang/protobuf/proto"
        "github.com/jhump/protoreflect/desc/protoparse"
        "github.com/jhump/protoreflect/desc/protoprint"
        "github.com/jhump/protoreflect/dynamic"
    )
    
    func main() {
    
        Filename := "./proto/test.proto"
    
        Parser := protoparse.Parser{}
            //加载并解析 proto文件,得到一组 FileDescriptor
        descs, err := Parser.ParseFiles(Filename)
        if err != nil {
            fmt.Printf("ParseFiles err=%v", err)
            return
        }
            
           //这里的代码是为了测试打印
        Printer := &protoprint.Printer{}
        var buf bytes.Buffer
        Printer.PrintProtoFile(descs[0], &buf)
        fmt.Printf("descsStr=%s\n", buf.String())
          
            //descs 是一个数组,这里因为只有一个文件,就取了第一个元素.
            //通过proto的message名称得到MessageDescriptor 结构体定义描述符
        msg := descs[0].FindMessage("test.AddFriendReq")
            //再用消息描述符,动态的构造一个pb消息体
        dmsg := dynamic.NewMessage(msg)
            
           //pb二进制消息 做反序列化 到 test.AddFriendReq 这个消息体
        err = dmsg.Unmarshal(GetMessageBin())
            
            //把test.AddFriendReq 消息体序列化成 JSON 数据
        jsStr, _ := dmsg.MarshalJSON()
        fmt.Printf("jsStr=%s\n", jsStr) 
    }
    
    //可能从远程服务得到一些二进制数据,这里为了方便测试,用本地序列化的pb
    func GetMessageBin() []byte {
        req := &testpb.AddFriendReq{
            Phone:   []string{"13145990022", "131313233"},
            Keyword: proto.String("I am good"),
        } 
        bin, err := proto.Marshal(req) 
        if err != nil {
            fmt.Printf("bin=%v,err=%v", bin, err)
        } 
        return bin
    }
    

    相关文章

      网友评论

          本文标题:Golang 加载 protobuffer 文件,动态解析 pb

          本文链接:https://www.haomeiwen.com/subject/huqykktx.html