在业务中经常有这种需求,给某某服务加一个命令字,用来接收的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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
}