在业务中经常有这种需求,给某某服务加一个命令字,用来接收的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" "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{} 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()) msg := descs[0].FindMessage("test.AddFriendReq") dmsg := dynamic.NewMessage(msg) err = dmsg.Unmarshal(GetMessageBin()) jsStr, _ := dmsg.MarshalJSON() fmt.Printf("jsStr=%s\n", jsStr) }
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 }
|