【笔记】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 ...
随机推荐
- CF1494B Berland Crossword 题解
Content 有一种叫做 Berland crossword 的拼图游戏.这个拼图由 \(n\) 行 \(n\) 列组成,你可以将里面的一些格子涂成黑色.现在给出 \(T\) 个这样的拼图,每个拼图 ...
- 贪心——55. 跳跃游戏 && 45.跳跃游戏II
给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 判断你是否能够到达最后一个位置. 示例 1: 输入: [2,3,1,1,4] 输出: true ...
- 模块化和webpack模块化打包
模块化和webpack模块化打包: 一.❀ 模块化 [导入import-----导出export] 1.为什么需要模块化? JavaScript 发展初期,代码简单地堆积在一起,只要能顺利地从上往下一 ...
- 解决appt.exe finished with non- zero exit value 1问题
解决appt.exe finished with non- zero exit value 1问题 最近使用Android Studio时,经常遇到appt.exe finished with non ...
- 【LeetCode】916. Word Subsets 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/word-sub ...
- 【LeetCode】394. Decode String 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 栈 日期 题目地址:https://leetcode ...
- JSON(JS 对象简谱,一种数据交换格式)
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式 存储和表示数据的文本格式 层次结构清晰.简洁 JSON是一个序列化的对象或数组 1.js ...
- Java 泛型通配符 T,E,K,V,?
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被 ...
- Dev C++调用汇编
参考: https://blog.csdn.net/ljx0305/article/details/5831742 https://www.cnblogs.com/jokerjason/p/95786 ...
- P4081 [USACO17DEC]Standing Out from the Herd P
知识点: 广义 SAM 原题面 Luogu 「扯」 随便「口胡」一下居然「过」了. 比较考验「代码能力」,第一次感觉「大模拟」没有白写((( 还有这个「符号」实在是太「上头」了. 前置知识 在线构造广 ...