为什么要反馈压力
服务端程序在遇到高并发请求时,一旦超过程序所能处理的极限,可能会导致崩溃,引发线上服务的大规模雪崩。压力反馈(Back Pressure
)是一种由服务端主动告知客户端自身资源不足,无法提供服务的一种手段。在 Web 服务中可以返回 HTTP 503
(Service Unavailable
)告知客户端当前服务器处于比较高的负载状态。这时客户端可以选择:
- 等待一定时间后进行重试
- 切换其他负载较低的节点或者服务器上
- 暂时停止使用此服务,对相关的业务进行降级处理
PHP-FPM 如何反馈压力
在传统 Nginx+PHP-FPM
服务端程序中,底层有 3
个关键的配置影响压力反馈:
php-fpm.conf
中pm.max_chindren
配置的最大进程数,如配置为200
表示最大启动200
个进程,一旦超过最大进程数,新的请求将不会被Accept
,而是进入到Listen Backlog
队列中进行排队,直到有空闲进程才会从队列弹出一个新的请求进行处理php-fpm.conf
中listen.backlog
配置Listen Backlog
队列长度,如配置为512
,则表示若没有空闲进程时,最大允许有512
个请求排队- 内核参数
net.core.somaxconn
,listen.backlog
设置的数值不一定是有效的,这取决于内核参数net.core.somaxconn
的设置,实际队列长度为min(listen.backlog, net.core.somaxconn)
,例如net.core.somaxconn=128
、listen.backlog=512
,实际的队列长度为128
而不是512
因此在 Nginx+PHP-FPM
程序中,最大并发为 pm.max_chindren + min(listen.backlog, net.core.somaxconn)
,一旦超过之后,就会拒绝新的请求,返回 502
错误,向客户端反馈压力。实际项目中,应当设置为一个合适的值,不宜过大或过小。否则就出产生客户端等时间过长,低负载拒绝请求两种问题。
Nginx
无法区分502
和503
错误,这是一个缺点
Swoole 程序如何反馈压力
由于 Swoole
是完全异步的架构,并发能力更强,在机器资源未耗尽的前提下,是可以无限接受、处理请求的。相比 Nginx+PHP-FPM
不好实现压力反馈,一般需要框架层面或者应用层代码中自行抛出 503
错误。Swoole
底层提供了多项配置可以解决一部分问题。
max_connection
限制最大连接数, 超过最大连接数之后,会拒绝新的连接。返回 502
错误给 Gateway
max_coroutine
限制最大进程数,Swoole
会在接收到客户端请求时创建一个新的协程进行处理,超过最大协程数之后,就会向客户端发送 HTTP 503
错误
max_concurrency
限制最大并发 HTTP
请求数,当前正在处理的请求数超过 max_concurrency
后,底层会立即向客户端发送 HTTP 503
错误,并关闭连接。
use Swoole\Http\Server;
$http = new Server('127.0.0.1', 9501);
$http->set([
'max_connection' => 10000,
'max_coroutine' => 50000,
'max_concurrency' => 500,
]);
$http->on('start', function ($server) {
echo "Swoole http server is started at http://127.0.0.1:9501\n";
});
$http->on('request', function ($request, $response) {
Co::sleep(0.1); // sleep 100ms 模拟请求处理的过程
$response->header('Content-Type', 'text/plain');
$response->end('Hello World');
});
$http->start();
启动服务器后使用 ab
或 wrk
压测。
wrk -c 1000 -d 5s http://127.0.0.1:9501/
Running 5s test @ http://127.0.0.1:9501/
2 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 70.89ms 132.48ms 1.10s 95.39%
Req/Sec 7.99k 3.68k 13.03k 66.32%
77521 requests in 5.09s, 5.41MB read
Socket errors: connect 0, read 55182, write 0, timeout 0
Non-2xx or 3xx responses: 55182
Requests/sec: 15242.49
Transfer/sec: 1.06MB
可以看到 5秒
之内,共发送了 77521
个请求,但是有 55182
个请求返回了 503
。这部分请求就是超过负载后压力反馈的结果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。