0%

MoE 系列[八] - 性能压测对比:Golang vs Lua vs Wasm

MoE 系列的前 7 篇主要介绍了用法,以及安全方面的实现机制。回答的是,如何使用,能否放心用的问题。随着用的人越来越多,咨询性能的问题也多起来了。

本文将提供一个经典网关鉴权(basic auth)的性能压测数据,希望能给大家一些参考。

需要说明的是,性能压测是一门很深的学问,也是跟场景强相关的,不仅仅跟被压测的场景有关,甚至跟运行时的系统环境也强相关。

本文并不旨在提供一个简单粗暴的结论,也不希望通过极端场景来带节奏,而是希望通过一个常见场景的数据,给大家一些相对客观的体感,以及一些定性上的参考。

压测场景

本次压测,选了两个插件场景,作为对比来体现不同方案的性能消耗点

  1. Passthrough

    插件里什么事情也不做,目的是体现插件框架的基础开销

  2. Basic auth 鉴权

    这是一个比较简单的场景,主要逻辑是,获取请求头,按照 basic auth 协议解析,主要是 base64 解码操作,做用户名 & 密码匹配

插件实现方案

除了 Golang 扩展,也选了 Lua 和 Wasm 作为对比,给大家一些参考

  1. Go 实现

    Go 代码只有一份,不过我们压测了 Go 1.20 和 1.21 rc 两个版本,因为 1.21 rc 里包含了一个我们搞了一年多的 cgo 优化
    这次我们也可以实际看看那个 cgo 优化的实际效果

  2. Lua 实现

    最开始只找了一个纯 Lua 的 base64 decode 函数,不过发现性能不太好,又找了个可以被 JIT 的 decode 函数,所以也是两个版本

  3. 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%

数据结论

  1. 从 Passthrough 的对比看,Lua 和 Wasm 的基础消耗比 Golang 低
  2. 从 Basic auth 的对比看,Golang 的整体性能反超 Lua 和 Wasm
  3. 切换到可以 JIT 的 Lua 代码,性能比 Golang 还好
  4. Golang 1.21 比 Golang 1.20 提升了 ~10%,cgo 优化效果确实是不错的

申明:上面的这些绝对数字,意义都不是很大,不同的运行环境可能都会有不少的差距

  1. Go1.20 中 cgo 涉及到的系统调用,在不同的系统上,性能损耗是不太一样的,有时候会有很大的出入
  2. Golang filter 的基础开销,也是跟系统相关的,之前压测过其他的系统,没有这么大(20%+)

个人点评

Lua

Lua 确实很小巧,基础开销不高,很适合这种嵌入式的场景。

如果你的需求很简单,又或者你对 Lua 代码的优化比较擅长,比如善于利用 LuaJIT,用 Lua 来实现插件应该是不错的

不过,通常来说,对于复杂的场景,要写出性能好的 Lua 代码,挑战还是比较大的,甚至还需要搞些 C 代码给 Lua 来调用

Golang

如果需求比较复杂,对于性能优化的奇技淫巧不太擅长,那么用 Golang 来实现插件就是很合适的选择

虽然,Golang 的基础开销要高一些,不过呢

  1. 在真实的场景里,还会有一些其他的业务插件逻辑,相对来说,这些基础开销比例就不那么高了
  2. 这些基础开销,我们也会持续优化,比如这次 Go 1.21 rc 效果就很明显

通常情况下,我们的需求都会比 basic auth 要复杂的,甚至我们还可能有多个需求,如果都是用 Go 来实现,那么这些基础开销,就会被分摊了。所以,复杂场景下,Go 的性能优势会更加明显。

Wasm

Wasm 的基础开销也不高,不过加上业务代码,性能就不太好了,比纯 Lua 还略差一点

或许是 tinygo 生成的 Wasm 不够高效,没有进一步分析了

最后

性能表现我们从一开始就很重视,也一直在优化,包括 Envoy Golang 自身的实现,以及 cgo 的优化。我们还有更多的优化在路上,或者在规划中。

本次的性能压测数据,只是披露了冰山一角,通常的性能压测还会关心内存,延时等,本文没有涉及,并不代表不重要。后续有时间,也会提供相关的压测分析。

最后,我是这么看待 Envoy Golang 的性能的:

  1. 眼下的性能,也是足够好的了,虽然有相对较高的基础开销,但是整体性能还是不错的
  2. 未来优化的空间是不小的,争取基础开销能降低到 Lua / Wasm 一个水准

如果你对本文内容有兴趣,欢迎联系,一起交流~