godx-007 / netpoll-benchmark

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

netpoll-benchmark

置顶声明:

RPC 场景测试 项目中,部分包含面向 Redis 场景evio, gnet 测试数据,不构成参考建议。

什么是 RPC 场景

这里所说的 RPC 场景,是为了区别于 Redis 场景,特点是处理逻辑(Handler)耗时较长。

以 Server 为例,一次服务调用过程可描述为:

接收数据(Read Buffer) -> 解析请求(Decode) -> 逻辑处理(Handler) -> 响应编码(Encode) -> 发送数据(Write Buffer)

如果上述过程处理速度很快(特别是 Handler), 则可以通过线程内串行处理的方式, 减少上下文切换,达到性能上的理论最优。 cache/proxy/纯计算类业务均属于这种情况,典型的如 nginx , redis 等。 由于没有专门的术语描述,以下我们将其称为 Redis 场景

而 RPC 业务特征是 Handler 逻辑较重,耗时较长,显然是不能串行处理的。 因此 RPC 场景,Handler 必须要异步处理,上下文切换、I/O 协作等都是必须考虑的代价。

Netpoll 解决什么问题

1. 过多长连接下的问题:

线上微服务场景中,由于上下游实例众多,上下游之间的连接数是 k(M x N) 的关系,导致 client/server 均持有大量长连接。 而由于数据分发的负载均衡,长连接的工作总是间歇式的,即存在一定比例的空闲连接,以下描述为"空闲率"。

当我们使用 go net 编写 server 端代码时,必然的为每个连接都分配了一个独立的 goroutine,理想情况下,协程空闲率 = 连接空闲率; 而在 netpoll 中,空闲连接是不持有 goroutine 的,只有正在工作的连接会在协程池中处理,这就尽可能的减少了协程数量,提高协程利用率。

我们构建了 测试场景 2 来评估空闲连接的代价。

2. 连接多路复用下的问题:

连接多路复用(以下简称 mux)是指在一条长连接上,合并「发送/接收」多个「请求/响应」。这种模式也是长连接利用率低下的一种解决方式。

mux 性能瓶颈主要在 合并包/拆分包,这里涉及到「并行转队列」和「队列转并行」的具体实现。

常见的基于 go net 编写的框架,会采用 go readloop(conn) && go writeloop(conn) 方式,构建两个协程,循环式(串行)的完成编解码,这在 go net 下确实是最优的。

但是 netpoll 有更优的实现,提供的 nocopy API 可以将编解码转为异步。为此,我们构建了 测试场景 3 来评估收益。

PS: netpoll 为什么不采用 串行编解码

A: 这里需要明确指出 串行编解码 适用场景更有限,在 LT(水平触发) 下,串行编解码 意味着需要快速判断包大小。然而并不是所有 RPC 协议都有头部来表明长度,比如 HTTP、Thrift Buffered 等,这些协议下,转异步编解码是效率更好的方式。

测试设定

我们认可 brpc 的测试观点,上下文切换是 RPC 场景 必须付出的代价,每个线程独立循环地收发数据,对真实场景缺乏指导意义。 我们希望 benchmark 可以指导后续的优化工作,为了更切合 RPC 场景 测试,引入了以下设定:

  1. 不能串行处理
    • 上述我们说明了 RPC 场景Redis 场景的区别,因此 Handler 必须异步执行。
    • time.Sleep 虽然能够规避串行处理,但 timer 唤醒拥有更高的调度优先级,干扰了 runtime 的正常调度,因此没有使用。
  2. 不能边收边发
    • RPC 场景,必须要接收到完整的数据包,才可以继续处理,不能接收到一半就开始回包。
    • evio, gnet 欠缺异步处理机制(异步关闭、资源清理等),因此我们做了近似处理,不构成参考建议
  3. 接收和发送的数据不能是同一份
    • RPC 场景 下的请求和响应,是通过序列化/反序列化产生的两份不同的数据。
    • 如果直接把读数据 buffer 直接写回,则忽略了这部分开销,并忽略了在这方面所做的一些优化。

开始测试

Echo 测试 将收到的 string 数据原样返回,定义了 Message 来模拟编解码过程。

协议格式为 header(4 Byte)(=payload length), payload(n Byte)

RPC 场景 在 Client 和 Server 端使用方式不同,因此分别测试两端的性能表现。

  • Client 测试:不同的 client,调用相同的 net server,评估 client 端性能表现。
  • Server 测试:相同的 net client,调用不同的 server,评估 server 端性能表现

测试场景 1

测试场景 1 模拟「长连接池模式」下的 Echo 测试,用来评估连接池场景的性能上限。

  • 连接池容量 = 1024

测试场景 2

测试场景 2 模拟「带空闲连接」下的 Echo 测试,用来评估空闲连接对性能的影响。

  • 连接空闲率 = 80%

测试场景 3

测试场景 3 模拟「连接多路复用模式」下的 Echo 测试,用来评估拆包/组包的实际效率。

  • 连接个数 = 4

快速执行:loopback 模式

以上测试场景,通过执行「脚本 + 场景号」,可以快速开始测试。执行前请先确认满足环境要求

  • client 测试
./scripts/benchmark_client.sh 1 # or 2, 3
  • server 测试
./scripts/benchmark_server.sh 1 # or 2, 3

环境要求

OS: Linux

  • 默认依赖了命令 taskset, 限定 client 和 server 运行的 CPU; 如在其他系统执行, 请修改脚本。

CPU: 推荐配置 >=20核, 最低要求 >=4核

  • 压测脚本默认需要 20核 CPU, 具体在脚本的 taskset -c ... 部分, 可以修改或删除。

参考数据 (echo size 1KB)

相关说明:

该压测数据是在调用端有充分机器资源压满服务端的情况下测试,更侧重于关注服务端性能。后续会提供调用端性能数据情况。

配置

  • CPU: Intel(R) Xeon(R) Gold 5118 CPU @ 2.30GHz
    • 运行限定 server 4CPU, client 16CPU
  • OS: Debian 5.4.56.bsk.1-amd64 x86_64 GNU/Linux
  • Go: 1.15.4

测试场景 1

QPS TP99 TP999
Server(4C) image image image
Client(16C) image image image

测试场景 2

QPS TP99 TP999
Server(4C) image image image
Client(16C) image image image

测试场景 3

QPS TP99 TP999
Server(4C) image image image
Client(16C) image image image

About


Languages

Language:Go 91.0%Language:Shell 5.7%Language:Python 3.3%