DeepFlow Wasm Plugin 性能调优实战
2023-12-27在之前的文章使用 DeepFlow Wasm 插件实现业务可观测性中,我们介绍了 DeepFlow 中的二次开发利器 —— Wasm Plugin。利用插件,我们可以实现很多个性化的应用协议解析目标,例如:
- 提取 HTTP Payload 中的错误码,并重写调用日志中的
response_code
、response_result
等字段 - 提取 Payload 中的交易流水号、用户 ID 等业务信息,用于增强调用日志和分布式追踪
- 解析使用 Protobuf、Thrift 等协议序列化的 Payload 信息
- 解析私有协议,生成调用日志
原生的 DeepFlow Agent 有着极高的应用协议解析性能,而 Wasm 程序通常也因其高性能著称。考虑到许多可观测性领域的工程师更熟悉 Golang,DeepFlow 特意提供了编写插件的 Golang SDK。在性能压测的过程中,我们发现针对插件代码的 GC 行为进行优化能有显著的成效,本文将优化思路分享给大家,帮助大家编写高性能的 Wasm Plugin。
0x0: 资源消耗摸底
我们使用这个 GitHub 仓库中的代码构造压测流量。压测程序会产生指定 TPS 速率的 HTTP 调用。我们将 client 和 server 运行在同一个虚拟机上,并在虚拟机上运行 deepflow-agent。在此基础上,我们向 deepflow-agent 下发一个 Wasm Plugin,它实现了解析 HTTP Json Payload 并提取错误码的功能,插件的代码在这个 GitHub 仓库里。
使用如下命令编译 Wasm Plugin:
1 | # 建议 go 版本不低于 1.21,tinygo 版本不低于 0.29 |
压力测试结果:
TPS | 是否开启 eBPF | 是否加载 Wasm 插件 | deepflow-agent CPU | deepflow-agent Memory |
---|---|---|---|---|
1.2K | 否 | 是 | 43.5% | 112MB |
2.5K | 否 | 是 | 97.6% | 112MB |
1.2K | 是 | 是 | 97.8% | 171MB |
1.2K | 否 | 否 | 0.71% | 102MB |
2.5K | 否 | 否 | 1.31% | 102MB |
1.2K | 是 | 否 | 3.54% | 144MB |
从前三行可以看到:当关闭 eBPF 时,deepflow-agent 若从 loopback 网卡上采集 1.2K TPS 的 HTTP 请求,此时消耗的 CPU 为单核的 43.5%;当开启 eBPF 时,压测程序生成的 1.2K TPS 会被 deepflow-agent 采集到三遍,loopback 网卡、客户端进程、服务端进程,此时 deepflow-agent 的 CPU 消耗为 97.8%。
从后三行可以看到,同样的 HTTP TPS 速率下,不加载 Wasm 插件时 deepflow-agent 的 CPU 消耗连 1/10 都不到,这说明插件造成了 deepflow-agent CPU 开销的显著增长。
0x1: 优化 TinyGo GC
调整 Wasm Plugin 的编译参数后我们发现,-gc
参数对资源消耗造成了显著的影响。我们找到了 nottinygc 库,希望能降低 GC 对资源开销的影响。
使用 nottinygc 需要做两个改造,首先在插件代码中需要进行 import:
1 | import _ "github.com/wasilibs/nottinygc" |
其次在编译插件时需要增加 -gc=custom
和 -tags=custommalloc
参数:
1 | tinygo build -o wasm.wasm \ |
上述编译参数解释如下:
-panic=trap
必须加,表示插件遇到 panic 不会传递到上层,否则插件 panic 会导致 deepflow-agent panic。-scheduler=none
使用 nottinygc 必须加, 否则不能加载。即使不使用 nottinygc 也建议加上。-gc
表示要使用的 gc,目前只有 nottinygc 和 precise 能避免内存泄漏,但是 precise 性能代价巨大。-no-debug
去掉 debug 信息,减少 wasm 文件大小
除此之外,还有一个 llvm 的编译参数 -llvm-features "+bulk-memory"
,理论上能提高性能,但实际测试差别不大,感兴趣的同学也可尝试看看对你的场景是否有提升帮助。
在使用 nottinygc 的过程中,我们还发现了一个 divide by zero 的 Bug,提交给了作者。上个月这个 Bug 已经被修复了,请注意使用正确的版本。
0x2: 使用 nottinygc 之后的压测结果
使用 nottinygc 之后我们重复了压测,结果如下:
TPS | 是否开启 eBPF | 是否加载 Wasm 插件 | deepflow-agent CPU | deepflow-agent Memory |
---|---|---|---|---|
1.2K | 否 | 是 | 7.83% | 110MB |
2.5K | 否 | 是 | 17.6% | 113MB |
1.2K | 是 | 是 | 27.5% | 162MB |
对比之前的测试结果,我们发现 nottinygc 能将 deepflow-agent 的 CPU 消耗从 43.5%、97.6%、97.8% 分别降低到 7.83%、17.6%、27.5%,效果显著。
0x3: 使用原生 Golang 编写插件
Tinygo 在编写插件的过程中有一些库的使用局限,相信大家一定希望可以使用原生 Golang 编写插件。今年 9 月分发布的 Golang v1.21 中第一次支持了 WASI。但是,目前这个特性还只是 Preview 版本,期待社区小伙伴的尝鲜使用报告,也让我们静候 Golang 官方对 WASI 的正式支持。
0x4: 什么是 DeepFlow
DeepFlow 是云杉网络开发的一款可观测性产品,旨在为复杂的云基础设施及云原生应用提供深度可观测性。DeepFlow 基于 eBPF 实现了应用性能指标、分布式追踪、持续性能剖析等观测信号的零侵扰(Zero Code
)采集,并结合智能标签(SmartEncoding
)技术实现了所有观测信号的全栈(Full Stack
)关联和高效存取。使用 DeepFlow,可以让云原生应用自动具有深度可观测性,从而消除开发者不断插桩的沉重负担,并为 DevOps/SRE 团队提供从代码到基础设施的监控及诊断能力。
GitHub 地址:https://github.com/deepflowio/deepflow
访问 DeepFlow Demo,体验零插桩、全覆盖、全关联的可观测性。