【笔记】golang中使用protocol buffers的底层库直接解码二进制数据
背景
一个简单的代理程序,发现单核QPS达到2万/s左右就上不去了,40%的CPU消耗在pb的decode/encode上面。
于是我想,对于特定的场景,直接从[]byte中取出字段,而不用完全的把整个结构在内存展开,岂不是要快很多。
so, 温习了一些PB二进制格式的知识。
pb的二进制格式:
参考的文章有:
几个关键点总结如下:
- 5 bit的 field index
- 3 bit的wire type
- wire type的定义如下:google.golang.org/protobuf/encoding/protowire/wire.go
const (
VarintType Type = 0 //int , float等全在这里
Fixed32Type Type = 5
Fixed64Type Type = 1
BytesType Type = 2 //字符串,或者嵌套的子类型
StartGroupType Type = 3 //废弃
EndGroupType Type = 4 //废弃
// Map 类型呢 ?
)
- 如果wire type 是 2, 则后续紧接着是长度信息
- bit 0 开头,说明用一个字节表示长度
- bit 10开头,说明2个字节表示长度
- bit 110开头,说明3个字节表示长度
- 以此类推……
- 如果wire type是 1或5,则很简单,后续的4字节或8字节是值
- 这个值被理解成int / uint / float等,就要看元数据的定义了
- 如果wire type 是 0,这里非常复杂
- 如果以 bit 0开头,只有 7 bit 表示值
- 如果以bit 10开头,后续的 14 bit 表示值
- 如果以bit 110开头,后续的 21 bit表示值
- 以此类推
- 值的内容以 Zigzag 编码 来表示
- 注意:二进制格式中唯一的元数据就是field index,除此之外不包含任何元数据信息。需要靠额外的元数据信息来指导如何decode这些二进制数据。
实操
PB二进制生成的代码:
import (
"github.com/golang/protobuf/proto"
"github.com/prometheus/prometheus/prompb"
"google.golang.org/protobuf/encoding/protowire"
)
func Test_make_pb(t *testing.T){
wr := &prompb.WriteRequest{
Timeseries: []prompb.TimeSeries{
{
Labels: []prompb.Label{
{
Name: "__name__",
Value: "test_metric_1",
},
{
Name: "job",
Value: "test1",
},
},
Samples: []prompb.Sample{
{
Value: 123.456,
Timestamp: int64(time.Now().UnixNano()) / 1000000,
},
},
},
},
Metadata: nil,
}
t.Logf("%s", wr.String())
buf, _ := proto.Marshal(wr)
t.Logf("\n%s\nlen=%d",
stringutil.HexFormat(buf), len(buf))
}
pb对应的二进制数据为:
0a 3b 0a 19 0a 08 5f 5f 6e 61 6d 65 5f 5f 12 0d | ; __name__
74 65 73 74 5f 6d 65 74 72 69 63 5f 31 0a 0c 0a | test_metric_1
03 6a 6f 62 12 05 74 65 73 74 31 12 10 09 77 be | job test1 w
9f 1a 2f dd 5e 40 10 a7 c6 90 f9 bd 2f | / ^@ /
假设我以JSON来描述上面的结构:
{
"id" :1,
"wire_type":2,
"body_len" : 55,
"child":[
{
"id" :1,
"wire_type":2,
"idx": 0,
"body_len" : 25,
"child":[
{
"id" :1,
"wire_type":2,
"body_len" : 8,
"value": "__name__",
},
{
"id":2,
"wire_type":2,
"body_len" : 13,
"value": "test_metric_1",
}
],
},
{
"id" : 1, //这个理解为属于第一组。这个节点和上个节点的ID都是1,因此反推出这两个节点属于repeated类型
"body_len" : 12,
"idx": 1,
"child":[
{
"id":1,
"body_len" : 3,
"value":"job"
},
{
"id":2,
"body_len" : 5,
"value":"test1"
},
]
},
{
"id": 2,
"wire_type":2,
"idx": 2,
"body_len": 12,
"child":[
{
"id":1,
"wire_type": 1, //64bit, float64
"value":"\x77\xbe\x9f\x1a\x2f\xdd\x5e\x40", //123.456
},
{
"id":2,
"wire_type":0, //timestamp
"value": "\xa7\xc6\x90\xf9\xbd\x2f"
}
]
}
]
}
后续打算基于PB的底层库来实现更高效率更少内存(但是非常非常难用)的库!
【笔记】golang中使用protocol buffers的底层库直接解码二进制数据的更多相关文章
- 【笔记】直接使用protocol buffers的底层库,对特定场景的PB编解码进行处理,编码性能提升2.4倍,解码性能提升4.8倍
接上一篇文章:[笔记]golang中使用protocol buffers的底层库直接解码二进制数据 最近计划优化prometheus的remote write协议,因为业务需要,实现了一个remote ...
- 【新手笔记】golang中使用protocol buffers 3
主要参考了这篇帖子:https://segmentfault.com/a/1190000009277748 1.下载windows版本的PB https://github.com/protocolbu ...
- 如何在 PHP 中处理 Protocol Buffers 数据
Protocol Buffers是谷歌定义的一种跨语言.跨平台.可扩展的数据传输及存储的协议,因为将字段协议分别放在传输两端,传输数据中只包含数据本身,不需要包含字段说明,所以传输数据量小,解析效率高 ...
- 在Android中使用Protocol Buffers(上篇)
本文来自网易云社区. 总览 先来看一下 FlatBuffers 项目已经为我们提供了什么,而我们在将 FlatBuffers 用到我们的项目中时又需要做什么的整体流程.如下图: 在使用 FlatBuf ...
- 在Android中使用Protocol Buffers(下篇)
本文来自网易云社区. FlatBuffers编码数组 编码数组的过程如下: 先执行 startVector(),这个方法会记录数组的长度,处理元素的对齐,准备足够的空间,并设置nested,用于指示记 ...
- golang gin框架中使用protocol buffers和JSON两种协议
首先,我使用protobuf作为IDL,然后提供HTTP POST + JSON BODY的方式来发送请求. 能不能使用HTTTP POST + PB序列化后的二进制BODY呢? 做了一下尝试,非常简 ...
- 在Android中使用Protocol Buffers(中篇)
本文来自网易云社区. FlatBuffers 编码原理 FlatBuffers的Java库只提供了如下的4个类: ./com/google/flatbuffers/Constants.java ./c ...
- Protocol Buffers学习笔记
Protocol Buffers学习笔记 1. 简介 Protocol Buffers是google发明的一种数据交换格式,独立于语言,独立于平台.与其他的数据交换格式有所不同,Protocol Bu ...
- protobuf Protocol Buffers 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
随机推荐
- 5、双指针技巧套路框架——Go语言版
前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...
- 我写了个IDEA开源插件,vo2dto 一键生成对象转换
让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...
- docker启动报错:Failed to Setup IP tables: Unable to enable SKIP DNAT rule
Creating network "kafka_default" with the default driverERROR: Failed to Setup IP tables: ...
- JAVA通过经纬度获取两点之间的距离
private static double EARTH_RADIUS = 6378.137; private static double rad(double d) { return d * Math ...
- 【LeetCode】22. Generate Parentheses 括号生成
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:括号, 括号生成,题解,leetcode, 力扣,Pyt ...
- Service有多个实现类,它怎么知道该注入哪个ServiceImpl类
方法一:Controller中注入service的时候使用@Autowired自动注入,@Qualifier("beanId")来指定注入哪一个. 方法二:Controller中注 ...
- Xcode Error returned in reply: Connection invalid
当电脑上有Xcode两个版本是使用其中的某一个的Simulator时出现了这样的报错 在Preference->Location->Command Line Tool 选择 Xcode 9 ...
- HDFS源码解析:教你用HDFS客户端写数据
摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...
- CS5265低成本替代RTD2172|CS5265替代兼容RTD2172|替代RTD2172
瑞昱RTD2172是TYPEC转HDMI4K60HZ音视频数据转换器芯片.CS5265可以替代兼容RTD2172,除了实现同等的转换功能外且整体方案成本和性价比方面比RTD2172要高,且外围器件较少 ...
- Java EE数据持久化框架笔记 • 【目录】
章节 内容 实践练习 Java EE数据持久化框架作业目录(作业笔记) 第1章 Java EE数据持久化框架笔记 • [第1章 MyBatis入门] 第2章 Java EE数据持久化框架笔记 • [第 ...