当前位置:网站首页>【微服务系列】Protocol buffer动态解析
【微服务系列】Protocol buffer动态解析
2022-06-26 06:13:00 【shanxiaoshuai】
最近的工作中用到了grpc。之前工作中使用的是基于thrift的微服务框架,对grpc不是很熟悉,只知道grpc是一个基于http2和protobuf的rpc框架。但是使用方法都是大同小异的,基于idl生成相应的文件,服务端的话就实现具体的service并对外提供服务,客户端的话需要引入client包发起rpc调用。
这里有个问题,就是要调用下游的服务需要引入下游的client,如果下游服务的idl发生变动的话我们需要在代码中更新client包,重新编译部署发布。在常规的业务开发中这种模式是没什么问题的,发布部署的节奏是跟随业务迭代的节奏来的。但是在某些场景下可能存在比较大的问题,比如一个网关服务,网关上注册了很多的业务,网关通过rpc来调用业务服务。在这种情况下如果每次业务变更idl都需要网关随着发布部署那显然是问题很大的。
针对上面的问题进行了调研,发现可以使用动态解析idl文件的方式来解决问题。可以使用github.com/jhump/protoreflect来实现pb和grpc的反射。本来主要介绍pb部分的内容。
代码示例
建立简单的proto文件如下,其中定义了简单的结构体HelloReq和HelloResp。
syntax = "proto3";
package test;
option go_package = "data/";
message HelloReq {
int64 ID = 1;
string Name = 2;
}
message HelloResp {
int64 Code = 1;
string Message = 2;
}
按照一般的做法,这时应该执行protoc --go_out=. ./test.proto命令生成test.pb.go,然后使用该结构体做相应的操作。
下面我们使用上面提到protoreflect包来进行运行时的解析,代码如下。
package main
import (
"encoding/json"
"github.com/jhump/protoreflect/desc/protoparse"
"github.com/jhump/protoreflect/dynamic"
"grpc_practice/data"
"log"
)
func main() {
// 创建parser对象
p := protoparse.Parser{
}
// 使用path的方式解析得到一些列文件描述对象,这里只有一个文件描述对象
fileDescs, err := p.ParseFiles("./test.proto")
if err != nil {
log.Printf("parse proto file failed, err = %s", err.Error())
return
}
// 从文件描述对象中根据消息名拿到消息藐视对象
helloReqDesc := fileDescs[0].FindMessage("test.HelloReq")
if helloReqDesc == nil {
log.Printf("no message matched")
return
}
// 根据消息描述对象生成动态消息
dMsg := dynamic.NewMessage(helloReqDesc)
// 使用该动态对象从GetHelloReq模拟的json对象解码
_ = dMsg.UnmarshalJSON(GetHelloReq())
log.Printf("req ID = %d, req Name = %s", dMsg.GetFieldByNumber(1), dMsg.GetFieldByNumber(2))
}
func GetHelloReq() []byte {
req := data.HelloReq{
}
req.ID = 1
req.Name = "xiaoshuai"
d, _ := json.Marshal(&req)
return d
}
原理解析
上面的demo还是比较简单的,看上面的代码不难发现核心的对象有两个,一是descriptor描述符,其对应了proto文件中的不同层级的对象;二是dynamicMessage动态消息,其代替了pb.go文件中的消息体。
简单来说的话,descriptor就是解析proto文件,并以数据结构的形式组织持有proto文件中的信息。dynamicMessage持有MessageDescriptor对象,就相当于了解该结构体所有信息。
Descriptor
不同层级的描述符的关系如下,没什么好详细展开的。
dynamicMessage
// Message is a dynamic protobuf message. Instead of a generated struct,
// like most protobuf messages, this is a map of field number to values and
// a message descriptor, which is used to validate the field values and
// also to de-serialize messages (from the standard binary format, as well
// as from the text format and from JSON).
type Message struct {
md *desc.MessageDescriptor
er *ExtensionRegistry
mf *MessageFactory
extraFields map[int32]*desc.FieldDescriptor
values map[int32]interface{
}
unknownFields map[int32][]UnknownField
}
dynamic message的源码如下,看到其主要的对象就是message descriptor,持有md对象dynamic message就拥有一个消息所有的信息,比如字段名称、序号、类型等。然后其值是存放在values这个map字段中,其值是以interface{}存放,所以用的时候会有大量的反射,后面会补充一些benchmark,观测其性能。
边栏推荐
- Market trend report, technical innovation and market forecast of microencapsulated chemical pesticides in China
- Print bit information of numbers
- TS泛型在函数、接口、类中使用介绍
- Self attention and multi head self attention (MSA) in transformer
- Understanding of nil in go language
- How to design a good technical scheme
- 06. talk about the difference and coding between -is and = = again
- Underlying principle of MySQL index
- 打印数字的位信息
- numpy. frombuffer()
猜你喜欢
Customer Stories | Netease spring breeze: the "spring breeze" of the fun industry, reaching out to all areas through in-depth interaction

DS18B20详解

Redis多线程与ACL
Alarm operation and Maintenance Center | build an efficient and accurate alarm collaborative processing system

消息队列-消息事务管理对比

How to select and build a real-time data warehouse scheme

Underlying principle of MySQL index

Hot! 11 popular open source Devops tools in 2021!

DPDK——TCP/UDP协议栈服务端实现(二)

Deeply uncover Ali (ant financial) technical interview process with preliminary preparation and learning direction
随机推荐
Alarm operation and Maintenance Center | build an efficient and accurate alarm collaborative processing system
typescript的class结合接口(interface)的简单使用
The difference between overload method and override method
DS18B20详解
A tragedy triggered by "yyyy MM DD" and vigilance before New Year's Day~
MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications
【Spark】Spark SQL 字段血缘如何实现
消息队列-消息事务管理对比
Pychart cannot run designer Exe (this application failed to start because no Qt platform plugin could be I appears)
ByteDance starts the employee's sudden wealth plan and buys back options with a large amount of money. Some people can earn up to 175%
Logstash——使用throttle过滤器向钉钉发送预警消息
TCP connection and disconnection, detailed explanation of state transition diagram
How to select and build a real-time data warehouse scheme
01 golang and matlab code of knapsack problem
typescript的type
自顶向下的变成方法
消息队列-功能、性能、运维对比
Deeply uncover Ali (ant financial) technical interview process with preliminary preparation and learning direction
Comparison between Prometheus and ZABBIX
稀疏数组sparsearray