Sogou C++ Workflow是一款性能优异的网络框架,本文介绍我们进行的性能测试, 包括方案、代码、结果,以及与其他同类产品的对比。
更多场景下的实验正在进行中,本文将持续更新。
HTTP Client/Server是Sogou C++ Workflow常见的应用场景, 我们首先对Server端进行实验。
我们部署了两台相同机器作为Server和Client,软硬件配置如下:
软硬件 | 配置 |
---|---|
CPU | 40 Cores, x86_64, Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz |
Memory | 192GB |
NIC | 25000Mbps |
OS | CentOS 7.8.2003 |
Kernel | Linux version 3.10.0-1127.el7.x86_64 |
GCC | 4.8.5 |
两者间ping
测得的RTT为0.1ms左右。
我们选择nginx和brpc作为对照组。 选择前者是因为它在生产中部署十分广泛,性能不俗; 对于后者,我们在本次实验中只关注HTTP Server方面的能力, 其他的特性已有单独的实验进行更为详尽的测试。
事实上,我们也对此二者之外的其他某些框架同时进行了实验, 但结果其性能表现相差较远,因此未在本文中体现。
后续我们将选取更多合适的框架加入对比测试中。
本次实验我们使用的压测工具为wrk和wrk2。 前者适合测试特定并发下的QPS极限和延时, 后者适合在特定QPS下测试延时分布。
我们也尝试过使用其他测试工具,例如ab等,但无法打出足够的压力。 有鉴于此,我们也在着手开发基于Sogou C++ Workflow的benchmark工具。
一般而言,对网络框架的性能测试,切入的角度可谓纷繁多样。 通过控制不同的变量、观测不同的指标,可以探究程序在不同场景下的适应能力。
本次实验,我们选择其中最普遍常见的变量和指标: 通过控制Client并发度和承载数据的大小,来测试QPS和延时的变化情况。 另外,我们还测试了在掺杂慢请求的正常请求的延时分布。
下面依次介绍两个测试场景。
我们搭建了一个极其简约的HTTP服务器, 忽略掉所有的业务逻辑, 将测试点聚焦在纯粹的网络框架性能上。
代码片段如下, 完整代码移步这里。
// ...
auto * resp = task->get_resp();
resp->add_header_pair("Date", timestamp);
resp->add_header_pair("Content-Type", "text/plain; charset=UTF-8");
resp->append_output_body_nocopy(content.data(), content.size());
// ...
可以从上述代码中看到,
对于到来的任何HTTP请求,
我们都会返回一段固定的内容作为Body,
并设置必要的Header,
包括代码中指明的content-type
、date
,
以及自动填充的connection
和content-length
。
HTTP Body的固定内容是在Server启动时随机生成的ASCII字符串, 其长度可以通过启动参数配置。 同时可以配置的还有使用的poller线程数和监听的端口号。 前者我们在本次测试中固定为16, 因此Sogou C++ Workflow将使用16个poller线程和20个handler线程(默认配置)。
对于nginx和brpc, 我们也构建了相同的返回内容, 并为nginx配置了40个进程、 brpc配置了40个线程。
我们控制并发度在[1, 2K]
之间翻倍增长,
数据长度在[16B, 64KB]
之间翻倍增长,
两者正交。
鉴于并发度和数据长度组合之后数量较多, 我们选择其中部分数据绘制为曲线。
上图可以看出,当数据长度保持不变, QPS随着并发度提高而增大,后趋于平稳。 此过程中Sogou C++ Workflow一直有明显优势, 高于brpc和nginx。 特别是数据长度为64和512的两条曲线, 并发度足够的时候,可以保持500K的QPS。
注意上图中nginx-64与nginx-512的曲线重叠度很高, 不易辨识。
上图可以看出,当并发度保持不变, 随着数据长度的增长, QPS保持平稳至4K时下降。 此过程中,Sogou C++ Workflow也一直保持优势。
上图可以看出,保持数据长度不变, 延时随并发度提高而有所上升。 此过程中,Sogou C++ Workflow略好于brpc, 大好于nginx。
上图可以看出,并发度保持不变时, 增大数据长度,造成延时上升。 此过程中,Sogou C++ Workflow好于nginx, 好于brpc。
我们在上一个测试的基础上,简单添加了一个慢请求的逻辑, 模拟业务场景中可能出现的特殊情况。
代码片段如下, 完整代码请移步这里。
// ...
if (std::strcmp(uri, "/long_req/") == 0)
{
auto timer_task = WFTaskFactory::create_timer_task(microseconds, nullptr);
series_of(task)->push_back(timer_task);
}
// ...
我们在Server的process里进行判断,
如果访问的是特定的路径,
则添加一个WFTimerTask
到Series的末尾,
能够模拟一个异步耗时处理过程。
类似地,对brpc使用bthread_usleep()
函数进行异步睡眠。
在本次实验中,我们固定并发度为1024,数据长度为1024字节, 分别以QPS为20K、100K和200K进行正常请求测试, 测绘延时; 与此同时,有另一路压力,进行慢请求, QPS是上述QPS的1%, 数据不计入统计。 慢请求的时长固定为5ms。
从上图可以看出,当QPS为20K时, Sogou C++ Workflow略次于brpc; 当QPS为100K时,两者几乎相当; 当QPS为200K时,Sogou C++ Workflow略好于brpc。 总之,可以认为两者在这方面旗鼓相当。