workerman 和 swoole 框架压测

分类: 兴趣杂文 标签: 框架压测 swoole workerman

业务需求

最近需要把业务端的请求从逻辑服务框架里拿出来,异步执行。 业务代码是 workerman gateway-worker 分布式框架。 本来用的是 workerman/http-client 发起异步请求的,http-client 是借助服务器的事件循环达到的异步请求功能。 也就是说 业务进程是 24个,那么目前有1000个链接处理,2000个 http请求,框架会把这2000个请求做分片。 由24个进程,每个进程轮询执行http请求。并不会阻塞线程。 但是好死不死的是 由其他服务商api不兼容 http-client 。导致偶有超时的请求。 后来发现社区里也有人有这样的问题,怀疑可能是 http-client 兼容问题,但是我用http-client 请求自己的业务api从未发生过异常。

随寻求其他解决方案,这个时候就想到了swoole的协程。 携程的逻辑也很简单,同样和 http-client 实现逻辑类似,携程类似缩小版本的线程。但是线程是由cpu调度的。而协程实由语言自己调度的。swoole的协程自然是swoole自己调度了。

之后就是压测环节,一开始 worker 数量一致都是20,然后请求延时站点,swoole 完成了对 workerman的碾压式胜利。 当然 workerman 也没有使用 workerman/http-client 发起请求,主要是兼容性不好。

第一轮测试中结果如下

| 框架 | 并发 QPS | CPU占用 |
| -------- | -------- |
| workerman    |   4.02   |16.0   |
| swoole    |   11.15   |91.3   |

我个人感觉非常奇怪,因为之前做过UDP压测,workerman是可以把CPU压到100%的。 随后,我把worker线程调到 200.进行了 二次压测。结果如下。(swoole 没有二次压测,其实结果差不多。)

| 框架 | 并发 QPS | CPU占用 |
| -------- | -------- |
| workerman    |   13.79   |86.6  |
| swoole    |   11.15   |91.3   |

可以看到 workerman 的性能确实上来了,但是我对线程进行状态查看。 发现200进程,只有不到一半是繁忙状态,剩下大多数线程处于空闲状态。 对此我并不清楚 workerman 的进程调用逻辑。

之后进行了空载测试效果如下。

| 框架 | 并发 QPS | CPU占用 |
| -------- | -------- |
| workerman    |   7921   |32.9  |
| swoole    |   7601   |32.8   |

结论就是

空载 workerman 比 swoole 略强。 因为没有管理协程逻辑。

负载 swoole 比 workerman 强,workerman精调也能实现swoole 类似的高并发。

swoole 的协程切换其实是由一定的性能损失。

另外理论上讲,每个请求都用协程处理,并发100个请求,每个请求3秒。是可以4秒钟内返回结果的。 但是压测中可以看到结果并不是想象中的样子。

最后要说的是 swoole 在处理代码的时候,处理跨协程调用,原子操作,避免资源竞争,调好参数也是挺稳定的。

压测代码

// swoole  压测代码
<?php
$server = new \Swoole\Http\Server('0.0.0.0', 9898);
$server->set($swooleOption = [
    'worker_num' =>  swoole_cpu_num() * 2,
    'enable_coroutine' => true,
    'hook_flags' => SWOOLE_HOOK_ALL
]);
$server->on('request', function ($request, $response) {
    Swoole\Coroutine::create(function () use ($response) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "https://httpbin.org/delay/3");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        $result = curl_exec($ch);
        $err = curl_error($ch);
        curl_close($ch);
        $response->end("hello word");
    });
});
$server->start();

// workerman 压测代码
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$http_worker = new Worker("http://0.0.0.0:9999");
$http_worker->count = 20;

$http_worker->onMessage = function (TcpConnection $connection, $request) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://httpbin.org/delay/3");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    $result = curl_exec($ch);
    $err = curl_error($ch);
    curl_close($ch);
    $connection->send("hello world");
};
Worker::runAll();

压测结果

# workerman work = 20  压测结果
root@utalk-pre:~# wrk -t20 -c100 -d30s --timeout 10s http://127.0.0.1:9999/
Running 30s test @ http://127.0.0.1:9999/
  20 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.56s     2.05s    9.83s    56.41%
    Req/Sec     2.25      3.50    20.00     88.79%
  121 requests in 30.10s, 15.60KB read
  Socket errors: connect 0, read 0, write 0, timeout 43
Requests/sec:      4.02
Transfer/sec:     530.69B

top - 17:17:43 up 28 days,  4:39,  5 users,  load average: 0.28, 2.33, 2.53
Tasks: 695 total,   1 running, 694 sleeping,   0 stopped,   0 zombie
%Cpu(s): 16.0 us,  1.8 sy,  0.0 ni, 81.7 id,  0.1 wa,  0.0 hi,  0.3 si,  0.0 st 
MiB Mem :   7270.7 total,    689.4 free,   2769.1 used,   4162.8 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   4501.6 avail Mem                                                                                                 

# swoole  swoole_cpu_num() x 2 压测结果
root@utalk-pre:/mnt/code/asyncTask# wrk -t30 -c100 -d60s --timeout 10s http://127.0.0.1:9999/
Running 1m test @ http://127.0.0.1:9999/
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.38s     1.66s    9.97s    66.97%
    Req/Sec     1.50      2.83    19.00     88.23%
  670 requests in 1.00m, 86.37KB read
  Socket errors: connect 0, read 0, write 0, timeout 16
Requests/sec:     11.15
Transfer/sec:      1.44KB


top - 17:08:35 up 28 days,  4:30,  5 users,  load average: 28.03, 8.26, 3.26
Tasks: 875 total,  68 running, 807 sleeping,   0 stopped,   0 zombie
%Cpu(s): 91.3 us,  7.2 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  1.5 si,  0.0 st
MiB Mem :   7270.7 total,    186.5 free,   3079.7 used,   4355.2 buff/cache    
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   4191.0 avail Mem 

# workerman work = 200 压测结果
root@utalk-pre:/mnt/code/asyncTask# wrk -t30 -c100 -d60s --timeout 10s http://127.0.0.1:9898/
Running 1m test @ http://127.0.0.1:9898/
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.09s     1.38s    9.81s    66.79%
    Req/Sec     1.14      2.54    10.00     89.92%
  829 requests in 1.00m, 131.96KB read
  Socket errors: connect 0, read 0, write 0, timeout 13
Requests/sec:     13.79
Transfer/sec:      2.20KB


top - 17:11:30 up 28 days,  4:33,  5 users,  load average: 2.70, 5.59, 3.14
Tasks: 686 total,   4 running, 682 sleeping,   0 stopped,   0 zombie
%Cpu(s): 86.6 us,  8.4 sy,  0.0 ni,  3.9 id,  0.0 wa,  0.0 hi,  1.2 si,  0.0 st 
MiB Mem :   7270.7 total,    671.5 free,   2787.1 used,   4162.9 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   4483.6 avail Mem     

空载压测代码

// workerman 
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$http_worker = new Worker("http://0.0.0.0:9999");
$http_worker->count = 20;
$http_worker->onMessage = function (TcpConnection $connection, $request) {
    $connection->send("hello world");
};
Worker::runAll();

// swoole
<?php
$server = new \Swoole\Http\Server('0.0.0.0', 9898);
$server->set($swooleOption = [
    'worker_num' =>  swoole_cpu_num() * 2,
    'enable_coroutine' => true,
    'hook_flags' => SWOOLE_HOOK_ALL
]);
$server->on('request', function ($request, $response) {
    $response->end("hello word");
});
$server->start();

空载压测结果

# workerman 空载测试结果
top - 17:25:57 up 28 days,  4:47,  4 users,  load average: 1.80, 0.89, 1.64
Tasks: 695 total,   6 running, 689 sleeping,   0 stopped,   0 zombie
%Cpu(s): 32.9 us, 44.6 sy,  0.0 ni,  3.9 id,  0.0 wa,  0.0 hi, 18.6 si,  0.0 st 
MiB Mem :   7270.7 total,    753.9 free,   2699.4 used,   4168.1 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   4571.3 avail Mem  

root@utalk-pre:/mnt/code/asyncTask# wrk -t30 -c100 -d60s --timeout 10s http://127.0.0.1:9999/
Running 1m test @ http://127.0.0.1:9999/
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    11.82ms    8.11ms 253.73ms   76.86%
    Req/Sec   265.26     55.70     1.01k    72.51%
  476089 requests in 1.00m, 59.93MB read
Requests/sec:   7921.61
Transfer/sec:      1.00MB

# swoole 空载测试结果
top - 17:53:30 up 28 days,  5:15,  3 users,  load average: 2.34, 0.57, 0.78
Tasks: 677 total,   7 running, 670 sleeping,   0 stopped,   0 zombie
%Cpu(s): 32.8 us, 43.9 sy,  0.0 ni,  3.8 id,  0.0 wa,  0.0 hi, 19.5 si,  0.0 st 
MiB Mem :   7270.7 total,    781.4 free,   2668.4 used,   4171.6 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   4602.3 avail Mem   

root@utalk-pre:~# wrk -t30 -c100 -d60s --timeout 10s http://127.0.0.1:9898/
Running 1m test @ http://127.0.0.1:9898/
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    12.17ms    7.45ms  93.01ms   71.06%
    Req/Sec   254.44     46.18   676.00     72.65%
  456831 requests in 1.00m, 71.01MB read
Requests/sec:   7601.34
Transfer/sec:      1.18MB