MoE 系列的前 7 篇主要介绍了用法,以及安全方面的实现机制。回答的是,如何使用,能否放心用的问题。随着用的人越来越多,咨询性能的问题也多起来了。
本文将提供一个经典网关鉴权(basic auth)的性能压测数据,希望能给大家一些参考。
需要说明的是,性能压测是一门很深的学问,也是跟场景强相关的,不仅仅跟被压测的场景有关,甚至跟运行时的系统环境也强相关。
本文并不旨在提供一个简单粗暴的结论,也不希望通过极端场景来带节奏,而是希望通过一个常见场景的数据,给大家一些相对客观的体感,以及一些定性上的参考。
压测场景
本次压测,选了两个插件场景,作为对比来体现不同方案的性能消耗点
Passthrough
插件里什么事情也不做,目的是体现插件框架的基础开销
Basic auth 鉴权
这是一个比较简单的场景,主要逻辑是,获取请求头,按照 basic auth 协议解析,主要是 base64 解码操作,做用户名 & 密码匹配
插件实现方案
除了 Golang 扩展,也选了 Lua 和 Wasm 作为对比,给大家一些参考
Go 实现
Go 代码只有一份,不过我们压测了 Go 1.20 和 1.21 rc 两个版本,因为 1.21 rc 里包含了一个我们搞了一年多的 cgo 优化
这次我们也可以实际看看那个 cgo 优化的实际效果Lua 实现
最开始只找了一个纯 Lua 的 base64 decode 函数,不过发现性能不太好,又找了个可以被 JIT 的 decode 函数,所以也是两个版本
Wasm 实现
基于 tetratelabs 开源的 proxy-wasm-go-sdk ,用 tinygo 来编译
代码实现:https://github.com/doujiang24/envoy-filter-benchmark/
值得一提的是,这些插件代码,都只是简单的复用了现成的代码,并没有经过特定的优化,主打的是快速实现。
压测环境
机器:阿里云 ecs.c7.large 机型,2 core CPU & 4 G memory
Envoy 版本:1.26.1
代理上游:Nginx,响应返回 Hello, world
压测取值:CPU 打满,QPS 极限值
统一使用 wrk 压测,单独的机器上执行
1 | wrk -d 3 -t 4 -c 100 http://localhost:10000 -H 'Authorization: Basic Zm9vbmFtZTp2YWxpZHBhc3N3b3Jk' |
如果有兴趣的话,可以通过上面的代码仓库来复现压测结论
压测数据
场景 | 极限 QPS | 相对基准 |
---|---|---|
裸 Envoy 反向代理 | 23670 | 100% |
Passthrough - Golang 1.20 | 16454 | 70% |
Passthrough - Golang 1.21 rc | 17877 | 76% |
Passthrough - Lua 插件 | 21195 | 90% |
Passthrough - Wasm | 20802 | 88% |
Basic auth - Golang 1.20 | 15445 | 65% |
Basic auth - Golang 1.21 rc | 16975 | 72% |
Basic auth - Lua 插件 | 14622 | 62% |
Basic auth - Lua 插件(JIT) | 19434 | 82% |
Basic auth - Wasm | 14031 | 59% |
数据结论
- 从 Passthrough 的对比看,Lua 和 Wasm 的基础消耗比 Golang 低
- 从 Basic auth 的对比看,Golang 的整体性能反超 Lua 和 Wasm
- 切换到可以 JIT 的 Lua 代码,性能比 Golang 还好
- Golang 1.21 比 Golang 1.20 提升了 ~10%,cgo 优化效果确实是不错的
申明:上面的这些绝对数字,意义都不是很大,不同的运行环境可能都会有不少的差距
- Go1.20 中 cgo 涉及到的系统调用,在不同的系统上,性能损耗是不太一样的,有时候会有很大的出入
- Golang filter 的基础开销,也是跟系统相关的,之前压测过其他的系统,没有这么大(20%+)
个人点评
Lua
Lua 确实很小巧,基础开销不高,很适合这种嵌入式的场景。
如果你的需求很简单,又或者你对 Lua 代码的优化比较擅长,比如善于利用 LuaJIT,用 Lua 来实现插件应该是不错的
不过,通常来说,对于复杂的场景,要写出性能好的 Lua 代码,挑战还是比较大的,甚至还需要搞些 C 代码给 Lua 来调用
Golang
如果需求比较复杂,对于性能优化的奇技淫巧不太擅长,那么用 Golang 来实现插件就是很合适的选择
虽然,Golang 的基础开销要高一些,不过呢
- 在真实的场景里,还会有一些其他的业务插件逻辑,相对来说,这些基础开销比例就不那么高了
- 这些基础开销,我们也会持续优化,比如这次 Go 1.21 rc 效果就很明显
通常情况下,我们的需求都会比 basic auth 要复杂的,甚至我们还可能有多个需求,如果都是用 Go 来实现,那么这些基础开销,就会被分摊了。所以,复杂场景下,Go 的性能优势会更加明显。
Wasm
Wasm 的基础开销也不高,不过加上业务代码,性能就不太好了,比纯 Lua 还略差一点
或许是 tinygo 生成的 Wasm 不够高效,没有进一步分析了
最后
性能表现我们从一开始就很重视,也一直在优化,包括 Envoy Golang 自身的实现,以及 cgo 的优化。我们还有更多的优化在路上,或者在规划中。
本次的性能压测数据,只是披露了冰山一角,通常的性能压测还会关心内存,延时等,本文没有涉及,并不代表不重要。后续有时间,也会提供相关的压测分析。
最后,我是这么看待 Envoy Golang 的性能的:
- 眼下的性能,也是足够好的了,虽然有相对较高的基础开销,但是整体性能还是不错的
- 未来优化的空间是不小的,争取基础开销能降低到 Lua / Wasm 一个水准
如果你对本文内容有兴趣,欢迎联系,一起交流~