為什麼要反饋壓力
服務端程式在遇到高併發請求時,一旦超過程式所能處理的極限,可能會導致崩潰,引發線上服務的大規模雪崩。壓力反饋(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
。這部分請求就是超過負載後壓力反饋的結果。