線上運作了一套輔助系統是利用了開源的PHP改的,之前也沒怎麼玩過PHP,沒想到這玩意兒還是有不少坑的。突然某一天一個使用者做線上活動推廣,然後短時間内湧進來了上萬的請求,然後資料庫連接配接耗盡,短時間内幾乎拖垮了整個系統。導緻系統奔潰的有多方面原因,今天主要針對PHP沒有資料庫連接配接池的原因來分析。
在PHP裡,資料庫連接配接在請求到達時建立,請求結束時釋放。如果同時幾千個請求到達,那就同時會建立幾千個資料庫連接配接,非常恐怖。而且PHP木有比較好的資料庫連接配接池驅動方案,是以我們得另想辦法。
解決這種問題有三種辦法:
1.使用Mysql Proxy中間件。Mysql Proxy提供了連接配接池管理的功能。但是我們沒有采用此方法,因為情況緊急,沒有人熟悉這玩意兒。
2.使用PHP-FPM。PHP-FPM是PHP的一個FastCGI程序管理器。通過配置可以控制同時處理PHP請求的程序數。
具體可以參考:http://www.linuxde.net/2013/06/14638.html
但是我們也沒用使用此方案,因為安裝配置過程比較麻煩。
3.使用Nginx的ngx_http_limit_req_module來控制請求。
此子產品可以通過自定義的鍵值來限制請求頻率。限制的方法就像漏鬥,每秒固定處理請求數,然後推遲超出的請求,最後超出最大值的直接503傳回拒絕。
我們使用了此方案,是因為隻需簡單配置,而且可以靈活控制限制請求的場景。例如,對于靜态資源的請求我們不做限制,而對于PHP的請求做限制。還可以從URL位址裡提取出變量資訊作為鍵,來達到更細的請求限制。
下面貼部分我們的配置給大家講解下。
http {
......
limit_req_zone $limit_key zone=limit_one:m rate=r/s;
#定義limit_key為Key的變量名,用于後面指派,每個Key都有自己的計數器。limit_one為zone的名稱。rate表示每秒最多接受30個同時請求。
server {
......
if ( $request_uri ~* .*php.* ) {
set $limit_one $binary_remote_addr;
#對于全部PHP首先有個預設的Key,使用用戶端的IP作為Key。相當于每個用戶端IP都會在zone的限制内。
}
if ( $query_string ~* .*id/(\d+)\.php.* ) {
set $limit_one $1;
#提取id後面的值作為Key。
}
if ( $query_string ~* .*appid/wx(.*)\.html.* ) {
set $limit_one $1;
#提取appid作為Key。
}
limit_req zone=limit_one burst=;
#限制limit_one在此server内的漏鬥容量為200。假設一個Key對應的請求數為200,那麼第一秒内在處理的為30個請求,其餘的170個請求在等待排隊。假設一個Key對應的請求數為300,那麼超出200的部分将直接傳回503。
.......
}
}
參考:http://www.ttlsa.com/nginx/nginx-limiting-the-number-of-requests-ngx_http_limit_req_module-module/
官方文檔:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html