一個關於nginx+php 的性能優化小測試

準備工做

  • 一臺ECS服務器
  • 手動編譯nginx+php
  • 修改index.php ,輸出 ‘hello world’
  • ab工具,ab -c 100 -n 50000 ,連續5次,記錄壓測的qps平均值。
  • 想辦法去優化,調整各類參數。每次調整一個參數有發現QPS提升,那就記錄下來,並思考qps瓶頸是在哪

Nginx 一些基本配置的描述

user administrator administrators;  #配置用戶或者組,默認爲nobody nobody。
 worker_processes 2;  #容許生成的進程數,默認爲1
 pid /nginx/pid/nginx.pid;   #指定nginx進程運行文件存放地址
 error_log log/error.log debug;  #制定日誌路徑,級別。這個設置能夠放入全局塊,http塊,server塊,級別以此爲:debug|info|notice|warn|error|crit|alert|emerg
 events {
     accept_mutex on;   #設置網路鏈接序列化,防止驚羣現象發生,默認爲on
     multi_accept on;  #設置一個進程是否同時接受多個網絡鏈接,默認爲off
     #use epoll; #事件驅動模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
     worker_connections  1024;    #最大鏈接數,默認爲512
 }
 http {
     include       mime.types;   #文件擴展名與文件類型映射表
     default_type  application/octet-stream; #默認文件類型,默認爲text/plain
     #access_log off; #取消服務日誌 
     log_format myFormat '$remote_addr$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定義格式
     access_log log/access.log myFormat;  #combined爲日誌格式的默認值
     sendfile on;   #容許sendfile方式傳輸文件,默認爲off,能夠在http塊,server塊,location塊。
     sendfile_max_chunk 100k;  #每一個進程每次調用傳輸數量不能大於設定的值,默認爲0,即不設上限。
     keepalive_timeout 65;  #鏈接超時時間,默認爲75s,能夠在http,server,location塊。
 upstream mysvr {   
       server 127.0.0.1:7878;
       server 192.168.10.121:3333 backup;  #熱備
     }
     error_page 404 https://www.baidu.com; #錯誤頁
     server {
         keepalive_requests 120; #單鏈接請求上限次數。
         listen       4545;   #監聽端口
         server_name  127.0.0.1;   #監聽地址 
         location  ~^.+$ {       #請求的url過濾,正則匹配,~爲區分大小寫,~爲不區分大小寫。
            #root path; #根目錄
            #index vv.txt; #設置默認頁
            proxy_pass  http://mysvr;  #請求轉向mysvr 定義的服務器列表
            deny 127.0.0.1;  #拒絕的ip
            allow 172.18.5.54; #容許的ip 
         } 
     }
}

 

接下來咱們開始吧

ECS配置
CPU: 1核
內存: 1 GiB
操做系統: CentOS 7 64位
當前使用帶寬: 1Mbpsphp

在高併發的狀況下,內核會認爲系統受到了SYN flood攻擊,會發送cookies(possible SYN flooding on port 80. Sending cookies),這樣會減慢影響請求的速度,因此在應用服務器上設置下這個參數爲0禁用系統保護就能夠進行大併發測試了:node

$ vim /etc/sysctl.conf 
 $ net.ipv4.tcp_syncookies = 0
 $ sysctl -p
 $ net.ipv4.tcp_syncookies = 0

 

net.ipv4.tcp_syncookies = 0  #此參數是爲了防止洪水攻擊的,但對於大併發系統,要禁用此設置
net.ipv4.tcp_max_syn_backlog #參數決定了SYN_RECV狀態隊列的數量,通常默認值爲512或者1024,即超過這個數量,系統將再也不接受新的TCP鏈接請求,必定程度上能夠防止系統資源耗盡。可根據狀況增長該值以接受更多的鏈接請求。
net.ipv4.tcp_tw_recycle #參數決定是否加速TIME_WAIT的sockets的回收,默認爲0。
net.ipv4.tcp_tw_reuse #參數決定是否可將TIME_WAIT狀態的sockets用於新的TCP鏈接,默認爲0。
net.ipv4.tcp_max_tw_buckets #參數決定TIME_WAIT狀態的sockets總數量,可根據鏈接數和系統資源須要進行設置nginx

默認編譯測試,無修改:

ab -c 100 -n 50000 http://127.0.0.1/index.php
一、 7111 二、7233 三、7240 四、7187 五、7197 平均:7194git

修改

worker_processes 1 => auto

worker_processes :工做進程數,調整通常爲auto或者跟cpu核心同樣的數量。github

通常一個進程足夠了,你能夠把鏈接數設得很大。(worker_processes: 1,worker_connections: 10,000)
若是有SSL、gzip這些比較消耗CPU的工做,並且是多核CPU的話,能夠設爲和CPU的數量同樣。(worker_processes: CPU核心數)
或者要處理不少不少的小文件,並且文件總大小比內存大不少的時候,也能夠把進程數增長,以充分利用IO帶寬(主要彷佛是IO操做有block)算法

一、 7242 二、7228 三、7275 四、7234 五、7231 平均:7242vim

worker_connections 1020 => 65535

單個進程容許的客戶端最大鏈接數,通常來講,鏈接數能夠設置和端口數同樣。瀏覽器

一、 7212 二、7236 三、7223 四、7260 五、7230 平均 7232緩存

worker_rlimit_nofile 65535

worker進程最大打開文件數,通常能夠優化設置成和端口數同樣bash

一、 7243 二、7236 三、7146 四、7243 五、7196 平均 7212.8

use epoll;

使用epoll的I/O模型,事件處理模型優化。

一、7265 二、7196 三、7227 四、7216 五、7253 平均 7231

multi_accept ON

multi_accept指令使得NGINX worker可以在得到新鏈接的通知時儘量多的接受鏈接。 此指令的做用是當即接受全部鏈接放到監聽隊列中。 若是指令被禁用,worker進程將逐個接受鏈接。
一、 7273 二、7281 三、7308 四、7299 五、7290 平均 7290

accept_mutex ON

因爲咱們在NGINX中配置了多個workers,所以咱們還應配置影響worker的相關指令。 events區域下accept_mutex參數將使每一個可用的worker進程逐個接受新鏈接。設置網路鏈接序列化,防止驚羣現象發生,默認爲on。
服務器是1核,因此影響不大
一、7268 二、7295 三、7308 四、7274 五、7261 平均 7281

tcp_nopush On

TCP_CORK做爲Nagle算法的替代方案,Linux提供了TCP_CORK選項。 該選項告訴TCP堆棧附加數據包,並在它們已滿或當應用程序經過顯式刪除TCP_CORK指示發送數據包時發送它們。 這使得發送的數據分組是最優量,而且所以提升了網絡的效率。
NGINX提供了tcp_nopush指令,在鏈接套接字時啓用TCP_CORK。 該指令可用於http,server和location區塊:
http{
     tcp_nopush on;
}
一、7309 二、7321 三、7292 四、7308 五、7322 平均 7310

tcp_nodelay on

TCP/IP網絡存在「小包」問題,其中單字符消息可能在高負載網絡上致使網絡擁塞。 例如分組大小爲41字節,其中40字節用於TCP報頭,只有1字節是有用信息。 這些小包占用了大約4000%的巨大開銷而且使得網絡飽和
ohn Nagle經過不當即發送小包來解決問題(Nagle的算法)。 全部這樣的分組被收集必定量的時間,而後做爲單個分組一次發送。 這改進了底層網絡的的效率。 所以,典型的TCP/IP協議棧在將數據包發送到客戶端以前須要等待200毫秒。
在打開套接字時可使用TCP_NODELAY選項來禁用Nagle的緩衝算法,並在數據可用時當即發送。 NGINX提供了tcp_nodelay指令來啓用此選項。 該指令可用於http,server和location區塊:
http{
     tcp_nodelay on;
}

一、 7326 二、7316 三、7334 四、7274 五、7290 平均 7308

worker_priority -5

指明worker進程的nice值
Linux系統中,優先級高的進程會佔用更多的系統資源,這裏配置的是進程的靜態優先級,取值範圍-20到+19,-20級別最高。所以能夠把這個值設置小一點,但不建議比內核進程的值低(一般爲-5)

測試中 0到-5的性能提高明顯 0可達到8000均值
一、 7982 二、8023 三、7932 四、7911 五、8052 平均 7980

php-fpm參數調優

pm = dynamic; 
表示使用哪一種進程數量管理方式
    dynamic表示php-fpm進程數是動態的,最開始是pm.start_servers指定的數量,若是請求較多,則會自動增長,保證空閒的進程數不小於pm.min_spare_servers,若是進程數較多,也會進行相應清理,保證多餘的進程數很少於pm.max_spare_servers
    static表示php-fpm進程數是靜態的, 進程數自始至終都是pm.max_children指定的數量,再也不增長或減小
  1.pm.start_servers = 15; 動態方式下的起始php-fpm進程數量
  2.pm.min_spare_servers = 5; 動態方式下的最小php-fpm進程數量
3.pm.max_spare_servers = 25; 動態方式下的最大php-fpm進程數量
4.pm.max_requests = 5000
設置每一個子進程重生以前服務的請求數. 對於可能存在內存泄漏的第三方模塊來講是很是有用的. 若是設置爲 ’0′ 則一直接受請求. 等同於 PHP_FCGI_MAX_REQUESTS 環境變量. 默認值: 0. 這段配置的意思是,當一個 PHP-CGI 進程處理的請求數累積到 5000 個後,自動重啓該進程。
7934 2 、8107 3 、8013 四、8039 五、7990 均值 8016

opcache

opcache 絕對是優化的利器,Opcache是字節碼緩存,也就是PHP在被編譯的時候,首先會把php代碼轉換爲字節碼,字節碼而後被執行。
php文件第二次執行時,一樣仍是會從新轉換爲字節碼,可是不少時候,文件內容幾乎是同樣的,好比靜態HTML文件,生成後內允許久都不會改變,用戶訪問請求直接由服務器讀取響應給客戶端瀏覽器。都不用通過PHP進行解析構建了。
內存中的字節碼數據,能夠直接緩存進行二次編譯。這樣程序就會快一些,cpu的消耗也少了。

結論

php-fpm 採用 prefork的方式 (listen同一個地址,而後fork出若干子進程),fast-cgi管理器實現的是多進程模型。

可是在php運行時,每個進程只能處理一個請求,實際上,運行時是單進程,單線程的。

php-fpm一個線程是阻塞模型,必須等待該客戶端請求php服務端返回數據,下一個nginx發過來的請求才能被受理,這個時候FPM就須要增多進程去應付併發,更高的qps 須要更多的進程處理,當處理請求的時候發生了時間較長阻塞,致使進程內存沒法釋放,後續請求沒有足夠的子進程去處理,更多的瓶頸在於 PHP-FPM 大量子進程的處理和消耗內存上面。

相關文章
相關標籤/搜索