优化 Go 语言数据打包:性能基准测试与分析

场景:在局域网内,需要将多个机器网卡上抓到的数据包同步到一个机器上。

原有方案:tcpdump -w 写入文件,然后定时调用 rsync 进行同步。

改造方案:使用 Go 重写这个抓包逻辑及同步逻辑,直接将抓到的包通过网络发送至服务端,由服务端写入,这样就减少了一次落盘的操作。

构造一个 pcap 文件很简单,需要写入一个 pcap文件头,后面每一条数据增加一个元数据进行描述。使用 pcapgo 即可实现这个功能,p.buffer[:ci.CaptureLength] 为抓包的数据。

ci := gopacket.CaptureInfo{CaptureLength: int(n),Length:        int(n),Timestamp:     time.Now(),
}
if ci.CaptureLength > len(p.buffer) {ci.CaptureLength = len(p.buffer)
}
w.WritePacket(ci, p.buffer[:ci.CaptureLength])

为了通过区分是哪个机器过来的数据包需要增加一个 Id,算上元数据和原始数据包,表达结构如下

// from github.com/google/gopacket
type CaptureInfo struct {// Timestamp is the time the packet was captured, if that is known.Timestamp time.Time `json:"ts" msgpack:"ts"`// CaptureLength is the total number of bytes read off of the wire.CaptureLength int `json:"cap_len" msgpack:"cap_len"`// Length is the size of the original packet.  Should always be >=// CaptureLength.Length int `json:"len" msgpack:"len"`// InterfaceIndexInterfaceIndex int `json:"iface_idx" msgpack:"iface_idx"`
}type CapturePacket struct {CaptureInfoId   uint32 `json:"id" msgpack:"id"`Data []byte `json:"data" msgpack:"data"`
}

有一个细节待敲定,抓到的包使用什么结构发送至服务端?json/msgpack/自定义格式?

json/msgpack 都有对应的规范,通用性强,不容易出 BUG,性能会差一点。自定义格式相比 json/msgpack 而言,可以去掉不必要的字段,连 key 都可以不用在序列化中出现,并且可以通过一些优化减少内存的分配,缓解gc压力。

自定义二进制协议优化思路如下

  1. CaptureInfo/Id 字段直接固定N个字节表示,对于 CaptureLength/Length 可以直接使用 2 个字节来表达,Id 如果数量很少使用 1 个字节来表达都可以

  2. 内存复用Encode 逻辑内部不分配内存,这样直接写入外部的 buffer,如果外部 buffer 是同步操作的话,整个逻辑 0 内存分配Decode 内部不分配内存,只解析元数据和复制 Data 切片,如果外部是同步操作,同样整个过程 0 内存分配如果是异步操作,那么在调用 Encode/Decode 的地方对 Data 进行复制,这里可以使用 sync.Pool 进行优化,使用四个 sync.Pool 分别分配 128/1024/8192/65536 中数据

sync.Pool 的优化点有两个

  • 异步操作下每个 Packet.Data 都需要有自己的空间,不能进行复用,使用 sync.Pool 来构造属于 Packet 的空间

  • 元数据序列化固定字节长度的 buffer,使用 make 或者数组都会触发 gc

func acquirePacketBuf(n int) ([]byte, func()) {var (buf   []byteputfn func())if n <= CapturePacketMetaLen+128 {smallBuf := smallBufPool.Get().(*[CapturePacketMetaLen + 128]byte)buf = smallBuf[:0]putfn = func() { smallBufPool.Put(smallBuf) }} else if n <= CapturePacketMetaLen+1024 {midBuf := midBufPool.Get().(*[CapturePacketMetaLen + 1024]byte)buf = midBuf[:0]putfn = func() { midBufPool.Put(midBuf) }} else if n <= CapturePacketMetaLen+8192 {largeBuf := largeBufPool.Get().(*[CapturePacketMetaLen + 8192]byte)buf = largeBuf[:0]putfn = func() { largeBufPool.Put(largeBuf) }} else {xlargeBuf := xlargeBufPool.Get().(*[CapturePacketMetaLen + 65536]byte)buf = xlargeBuf[:0]putfn = func() { xlargeBufPool.Put(xlargeBuf) }}return buf, putfn
}func (binaryPack) EncodeTo(p *CapturePacket, w io.Writer) (int, error) {buf := metaBufPool.Get().(*[CapturePacketMetaLen]byte)defer metaBufPool.Put(buf)binary.BigEndian.PutUint64(buf[0:], uint64(p.Timestamp.UnixMicro()))...return nm + nd, err
}

数据包构造大小(By 通义千问)

分析

  • Binary Pack

    对于较小的数据(72字节),编码后增加了22字节。

    对于较大的数据(16384字节),编码后增加了22字节。

    总体来看,Binary Pack的编码效率较高,增加的字节数相对较少。

  • MsgPack

    对于较小的数据(72字节),编码后增加了78字节。

    对于较大的数据(16384字节),编码后增加了79字节。

    MsgPack的编码效率在小数据量时不如Binary Pack,但在大数据量时仍然保持较高的效率。

  • Json Pack

    对于较小的数据(72字节),编码后增加了119字节。

    对于较大的数据(16384字节),编码后增加了5565字节。

    Json Pack的编码效率较低,特别是对于大数据量,增加的字节数较多。

  • Json Compress Pack

    对于较小的数据(72字节),编码后增加了123字节。

    对于较大的数据(16384字节),编码后增加了120字节。

    Json Compress Pack在小数据量时增加的字节数较多,但在大数据量时增加的字节数较少,表明压缩效果较好。

通过这个表格,你可以更直观地看到不同数据打包方法在不同数据量下的表现。希望这对你有帮助!

Benchmark

json

可以看到使用 buffer 进行复用提升比较明显,主要还是减少内存分配带来的提升。

BenchmarkJsonPack/encode#72-20                    17315143        647.1 ns/op         320 B/op      3 allocs/op
BenchmarkJsonPack/encode#1024-20                   4616841         2835 ns/op        1666 B/op      3 allocs/op
BenchmarkJsonPack/encode#16384-20                   365313        34289 ns/op       24754 B/op      3 allocs/op
BenchmarkJsonPack/encode_with_buf#72-20           24820188        447.4 ns/op         128 B/op      2 allocs/op
BenchmarkJsonPack/encode_with_buf#1024-20         13139395        910.6 ns/op         128 B/op      2 allocs/op
BenchmarkJsonPack/encode_with_buf#16384-20         1414260         8472 ns/op         128 B/op      2 allocs/op
BenchmarkJsonPack/decode#72-20                     8699952         1364 ns/op         304 B/op      8 allocs/op
BenchmarkJsonPack/decode#1024-20                   2103712         5605 ns/op        1384 B/op      8 allocs/op
BenchmarkJsonPack/decode#16384-20                   159140        73101 ns/op       18664 B/op      8 allocs/op

msgpack

同样看到使用 buffer 进行复用的提升,和 json 的分水岭大概在 1024 字节左右,超过这个大小 msgpack 速度快很多,并且在解析的时候内存占用不会随数据进行增长。

BenchmarkMsgPack/encode#72-20                     10466427         1199 ns/op         688 B/op      8 allocs/op
BenchmarkMsgPack/encode#1024-20                    6599528         2132 ns/op        1585 B/op      8 allocs/op
BenchmarkMsgPack/encode#16384-20                   1478127         8806 ns/op       18879 B/op      8 allocs/op
BenchmarkMsgPack/encode_with_buf#72-20            26677507        388.2 ns/op         192 B/op      4 allocs/op
BenchmarkMsgPack/encode_with_buf#1024-20          31426809        400.2 ns/op         192 B/op      4 allocs/op
BenchmarkMsgPack/encode_with_buf#16384-20         22588560        494.5 ns/op         192 B/op      4 allocs/op
BenchmarkMsgPack/decode#72-20                     19894509        654.2 ns/op         280 B/op     10 allocs/op
BenchmarkMsgPack/decode#1024-20                   18211321        664.0 ns/op         280 B/op     10 allocs/op
BenchmarkMsgPack/decode#16384-20                  13755824        769.1 ns/op         280 B/op     10 allocs/op

json压缩

在内网的情况下,带宽不是问题,这个压测结果直接被 Pass

BenchmarkJsonCompressPack/encode#72-20               19934       709224 ns/op     1208429 B/op     26 allocs/op
BenchmarkJsonCompressPack/encode#1024-20             17577       766349 ns/op     1212782 B/op     26 allocs/op
BenchmarkJsonCompressPack/encode#16384-20            11757       860371 ns/op     1253975 B/op     25 allocs/op
BenchmarkJsonCompressPack/decode#72-20              490164        28972 ns/op       42048 B/op     15 allocs/op
BenchmarkJsonCompressPack/decode#1024-20            187113        71612 ns/op       47640 B/op     23 allocs/op
BenchmarkJsonCompressPack/decode#16384-20            35790       346580 ns/op      173352 B/op     30 allocs/op

自定义二进制协议

对于序列化和反序列化在复用内存后,速度的提升非常明显,在同步的操作下,能做到 0 字节分配。异步场景下,使用 sync.Pool 内存固定字节分配(两个返回值在堆上分配)

BenchmarkBinaryPack/encode#72-20                  72744334        187.1 ns/op         144 B/op      2 allocs/op
BenchmarkBinaryPack/encode#1024-20                17048832        660.6 ns/op        1200 B/op      2 allocs/op
BenchmarkBinaryPack/encode#16384-20                2085050         6280 ns/op       18495 B/op      2 allocs/op
BenchmarkBinaryPack/encode_with_pool#72-20        34700313        109.2 ns/op          64 B/op      2 allocs/op
BenchmarkBinaryPack/encode_with_pool#1024-20      39370662        101.1 ns/op          64 B/op      2 allocs/op
BenchmarkBinaryPack/encode_with_pool#16384-20     18445262        177.2 ns/op          64 B/op      2 allocs/op
BenchmarkBinaryPack/encode_to#72-20              705428736        16.96 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/encode_to#1024-20            575312358        20.78 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/encode_to#16384-20           100000000        113.4 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/decode_meta#72-20           1000000000        2.887 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/decode_meta#1024-20         1000000000        2.882 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/decode_meta#16384-20        1000000000        2.876 ns/op           0 B/op      0 allocs/op
BenchmarkBinaryPack/decode#72-20                 100000000        85.63 ns/op          80 B/op      1 allocs/op
BenchmarkBinaryPack/decode#1024-20                 7252350        445.4 ns/op        1024 B/op      1 allocs/op
BenchmarkBinaryPack/decode#16384-20                 554329         5499 ns/op       16384 B/op      1 allocs/op
BenchmarkBinaryPack/decode_with_pool#72-20       109352595        33.97 ns/op          16 B/op      1 allocs/op
BenchmarkBinaryPack/decode_with_pool#1024-20      85589674        36.27 ns/op          16 B/op      1 allocs/op
BenchmarkBinaryPack/decode_with_pool#16384-20     26163607        140.4 ns/op          16 B/op      1 allocs/op

总结一下

通义千问的

Binary Pack:

- encode_to:性能最优,几乎没有内存分配,适用于高性能要求的场景。

- encode_with_pool:使用内存池优化,显著减少了时间和内存开销,适用于大多数场景。

- encode:标准方法,时间和内存开销较高。

MsgPack:

- encode_with_buf:使用预分配的缓冲区,显著减少了时间和内存开销,适用于大多数场景。

- encode:标准方法,时间和内存开销较高。

- decode:解码性能一般,内存开销较高。

Json Pack:

- encode_with_buf:使用预分配的缓冲区,显著减少了时间和内存开销,适用于大多数场景。

- encode:标准方法,时间和内存开销较高。

- decode:解码性能较差,内存开销较高。

Json Compress Pack:

- encode:标准方法,时间和内存开销非常高,不推荐用于高性能要求的场景。

- decode:解码性能较差,内存开销较高。

我总结的

在内网的环境进行传输,一般网络带宽不会成为瓶颈,所以可以不用考虑数据压缩,上面结果也看到压缩非常占用资源;如果对数据内容不关心且数据量非常多的情况下(比如传输 pcap 包),那么使用自定义协议可能更合适一些,固定长度的元数据解析起来优化空间巨大,二进制解析比 json/msgpack 快内存分配也非常少。

文章转载自:文一路挖坑侠

原文链接:https://www.cnblogs.com/shuqin/p/18427020

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149931.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

大势智慧亮相“第十届博博会”,展现数字文旅新质生产力!

2024年8月23日至26日&#xff0c;由国家文物局指导&#xff0c;中国博物馆协会主办的第十届“中国博物馆及相关产品与技术博览会”&#xff08;简称“博博会”&#xff09;在呼和浩特敕勒川国际会展中心成功举办。 呼和浩特敕勒川国际会展中心 大势智慧携数字文旅全栈解决方案以…

SpringBoot框架下的客户管理策略

1 绪论 1.1研究背景 随着网络不断的普及发展&#xff0c;企业客户管理系统依靠网络技术的支持得到了快速的发展&#xff0c;首先要从员工的实际需求出发&#xff0c;通过了解员工的需求开发出具有针对性的首页、个人中心、员工管理、客户信息管理、行业类型管理、项目信息管理、…

新能源汽车充电桩怎么选?

新能源汽车是我国七大战略性新兴产业之一&#xff0c;已成为汽车产业转型升级的重要推动力。毫无疑问。充电桩作为我国新能源汽车产业链下游的重要环节&#xff0c;在国家政策的大力支持和市场需求的带动下&#xff0c;有着非常广阔的前景。安科瑞叶西平187-06160015 新能源汽…

数据结构---顺序表之单链表

1.链表的概念 链表是一种逻辑上是线性的&#xff0c;但物理结构不一定是线性的数据结构&#xff0c;它通过链表中的指针链接次序实现的 链表的存储空间是我们通过动态内存开辟的内存空间&#xff0c;所以他们的地址可能是连续的也可能不是连续的 2.链表的分类 1.单向或者双向…

Footprint Analytics: 我们为何打造 Growthly 这款产品

在 Web3 的领域内&#xff0c;数据已成为新的“财富”。在 Footprint Analytics&#xff0c;我们始终站在区块链数据分析的最前沿&#xff0c;提供全方位的解决方案&#xff0c;为 Web3 生态中的企业和项目简化数据分析的复杂性。然而&#xff0c;随着我们对客户需求及行业趋势…

VLAN原理与接口

在学习之前&#xff0c;先抛出问题&#xff1a;什么是VLAN&#xff1f;VLAN工作原理是什么&#xff1f;VLAN如何配置&#xff1f; VLAN的定义 VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是一种在逻辑上将局域网设备从物理上划分的网络技…

一种单目标A*算法设计与实现

一种单目标A*算法设计与实现 作者&#xff1a;吴屏珊 最近在学习简单的单目标A*算法&#xff0c;其中在CSDN上阅读到的一篇博文给了我很大启发&#xff0c;于是在该博文的基础上&#xff0c;笔者记录了一点自己对于A*算法的体会和感悟。原文链接 目录 文章目录 一种单目标A*…

微信接口报错:Http头Authorization中的timestamp与 发起请求的时间不得超过5分钟

在发起请求的时候微信会自己封装一个系统时间 当服务器系统时间与实际实际相差5分钟或者5分钟以上就会出现这个错误 需要修改系统时间 问一下ChatGPT 用自动同步 sudo timedatectl set-ntp true 服务器执行 &#xff0c; 过个一分钟 再查看时间 发现正确 再支付就可以成功…

Spring6梳理11——依赖注入之注入List集合类型属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 11 依赖注入之注入List集合类型属性 11.1 创建实体类Emp以及Dept Dept类中添加了遍历Emp…

Java语法-类和对象(上)

1. 面向对象的初步认识 1.1 什么是面向对象 概念: Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。 1.2 面向对象VS面向过程 如:洗衣服 面向过程: 注重的是洗衣服的过程,少了一个环节也不…

七层负载均衡和四层负载均衡的区别

文章目录 什么是七层负载均衡&#xff1f;一、定义与工作原理二、优点与缺点三、应用场景四、常见七层负载均衡器五、负载均衡算法 什么是四层负载均衡&#xff1f;一、定义与原理定义&#xff1a;原理&#xff1a; 二、特点与应用场景特点&#xff1a;应用场景&#xff1a; 三…

python类的call方法与init方法

1. call方法 在对象被调用了的时候就会调用call方法a(666) class A:def __call__(self, args):print(call 方法被调用了,args) aA() a(666) 2.init方法 创建对象的时候 init 方法被调用 class A:def __init__(self,args):print(创建对象的时候 init 方法被调用了,args) aA(…

三、LLM应用开发准备工作

LLM应用开发准备工作 开发基础开发工具大模型kx上网key的配置与使用网站结语 开发基础 最好具备一定的Python开发基础&#xff0c;不需要特别深 如果不具备&#xff0c;可以先学习一下基础知识&#xff08;概念&#xff09;&#xff0c;比如Python环境管理、包管理与使用、基本…

城市酷选:如何四年做到3000亿销售额 会员超500w

城市酷选&#xff0c;这一融合了线上线下消费的会员制社交电商平台&#xff0c;正以其独特的运营模式在市场中崭露头角。该平台不仅汇聚了超过600万的会员与60万商家&#xff0c;更实现了年交易额的百亿突破&#xff0c;彰显了其强大的市场影响力和消费者吸引力。 创新排队免单…

必应bing推广kai户流程教学,满足企业获客需求

微软Bing广告提供了三种主要广告类型&#xff0c;可以满足大多数出海企业的展示和客户获取需求。 搜索广告&#xff08;Search Ads&#xff09; 包括标准搜索广告和动态搜索广告。当用户的搜索词与投放的关键词匹配时&#xff0c;相应的搜索广告会被触发&#xff0c;向用户展示…

基于深度学习的数字识别系统的设计与实现(python、yolov、PyQt5)

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

Nginx 的优化与防盗链设置

Nginx的优化 隐藏版本号 暴露版本号&#xff0c;攻击者就可以根据版本漏洞进行攻击。 查看版本号的方法&#xff1a; curl -I http://192.168.110.60 网页访问F12查看源代码 隐藏版本号的方法 方法一、修改配置文件 添加关闭版本号 vim /usr/local/nginx/conf/nginx.conf…

构建高效企业客户管理系统:SpringBoot应用

1 绪论 1.1研究背景 随着网络不断的普及发展&#xff0c;企业客户管理系统依靠网络技术的支持得到了快速的发展&#xff0c;首先要从员工的实际需求出发&#xff0c;通过了解员工的需求开发出具有针对性的首页、个人中心、员工管理、客户信息管理、行业类型管理、项目信息管理、…

心觉:早上醒来是先冥想还是先洗漱,如何提高冥想的质量

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作179/1000天 很多人在学习冥想的时候会有一个疑惑&#xff1f; 都知道在半睡半醒的状态下冥想效果最好 那么早上醒来之后&#xf…

【全新课程】正点原子《ESP32物联网项目实战》培训课程上线!

正点原子《ESP32物联网项目实战》全新培训课程上线啦&#xff01;正点原子工程师手把手教你学&#xff01;通过多个项目实战&#xff0c;掌握ESP32物联网项目的开发&#xff01; 一、课程介绍 本课程围绕物联网实战项目展开教学&#xff0c;内容循序渐进&#xff0c;涵盖了环…