业务需求
最近需要把业务端的请求从逻辑服务框架里拿出来,异步执行。 业务代码是 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