Nginx工做原理和優化、漏洞

Nginx (engine x) (外交部)

  • 高性能的HTTP和反向代理服務器
  • IMAP/POP3/SMTP服務器
  • 負載均衡服務器php

  • 輕量級的Web 服務器/反向代理服務器
  • 電子郵件(IMAP/POP3)代理服務器html

佔有內存少,併發能力強,事實上nginx的併發能力確實在同類型的網頁服務器中表現較好前端

結構與擴展

一個主進程和多個工做進程。工做進程是單線程的,且不須要特殊受權便可運行;mysql

HTTP基礎功能

處理靜態文件,索引文件以及自動索引;linux

反向代理加速(無緩存),簡單的負載均衡和容錯;nginx

FastCGI,簡單的負載均衡和容錯;web

模塊化的結構。過濾器包括gzipping,byte ranges,chunked responses,以及 SSI-filter。在SSI過濾器中,到同一個 proxy 或者 FastCGI 的多個子請求併發處理;sql

SSL 和 TLS SNI 支持;vim

IMAP/POP3代理服務功能:
使用外部 HTTP 認證服務器重定向用戶到 IMAP/POP3 後端;
使用外部 HTTP 認證服務器認證用戶後鏈接重定向到內部的 SMTP 後端;後端

其餘HTTP功能
基於名稱和基於IP的虛擬服務器;

安裝

模塊依賴性

gzip模塊須要 zlib 庫
rewrite模塊須要 pcre 庫
ssl 功能須要openssl庫

Nginx的模塊與工做原理

Nginx由 內核和模塊 組成
內核的設計很是微小和簡潔,完成的工做也很是簡單,僅僅經過查找配置文件將客戶端請求映射到一個 location block(location是Nginx配置中的一個指令,用於URL匹配),而在這個location中所配置的每一個指令將會啓動不一樣的模塊去完成相應的工做。

外部請求 → nginx內核處理(調用配置文件) → 基於配置文件選擇某個處理模塊 → 處理後的內容交給 過濾模塊 →響應外部請求

Nginx的模塊從結構上分爲核心模塊、基礎模塊和第三方模塊:

  • 核心模塊:HTTP模塊、EVENT模塊和MAIL模塊

  • 基礎模塊:HTTP Access模塊、HTTP FastCGI模塊、HTTP Proxy模塊和HTTP Rewrite模塊,

  • 第三方模塊:HTTP Upstream Request Hash模塊、Notice模塊和HTTP Access Key模塊。

用戶根據本身的須要開發的模塊都屬於第三方模塊

Nginx的模塊從功能上分爲以下三類:

  • Handlers(處理器模塊)。
    此類模塊直接處理請求,並進行輸出內容和修改headers信息等操做。Handlers處理器模塊通常只能有一個。

  • Filters (過濾器模塊)。
    此類模塊主要對其餘處理器模塊輸出的內容進行修改操做,最後由Nginx輸出。

  • Proxies (代理類模塊)。
    此類模塊是Nginx的HTTP Upstream之類的模塊,這些模塊主要與後端一些服務好比FastCGI等進行交互,實現服務代理和負載均衡等功能。

模塊能夠看作Nginx真正的勞動工做者。

handler模塊負責處理請求,完成響應內容的生成,而filter模塊對響應內容進行處理。

Nginx的模塊直接被編譯進Nginx,所以屬於靜態編譯方式。啓動Nginx後,Nginx的模塊被自動加載,不像Apache,首先將模塊編譯爲一個so文件,而後在配置文件中指定是否進行加載。
在解析配置文件時,Nginx的每一個模塊都有可能去處理某個請求,可是同一個處理請求只能由一個模塊來完成。

Nginx的進程模型

單工做進程
多工做進程

在單工做進程模式下,除主進程外,還有一個工做進程,工做進程是單線程的;
在多工做進程模式下,每一個工做進程包含多個線程。
Nginx默認爲單工做進程模式。

一個master進程和多個worker進程。

master進程 (nginx管家)

主要用來管理worker進程,包含:接收來自外界的信號,向各worker進程發送信號,監控worker進程的運行狀態,當worker進程退出後(異常狀況下),會自動從新啓動新的worker進程。

kill -HUP pid 重啓某個程序

輪休換班

./nginx -s reload,重啓nginx
./nginx -s stop,中止nginx的運行

worker進程:

而基本的網絡事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。

worker進程的個數通常咱們會設置與機器cpu核數一致

master進程裏面,先創建好須要listen的socket(listenfd)以後,而後再fork出多個worker進程。

一個worker進程在accept這個鏈接以後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開鏈接,這樣一個完整的請求就是這樣的了

nginx的進程模型:

Nginx+FastCGI運行原理

FastCGI是一個可伸縮地、高速地在HTTP server和動態腳本語言間通訊的接口。CGI 通用網關接口

多數流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等。同時,FastCGI也被許多腳本語言支持,其中就有PHP。

FastCGI接口方式採用C/S結構

Nginx+FastCGI運行原理

Nginx不支持對外部程序的直接調用或者解析,全部的外部程序(包括php)必須經過FastCGI接口來調用。

FastCGI接口在linux下是socket(這個socket能夠是文件socket,也能夠是ip socket)

wrapper:爲了調用CGI程序,還須要一個FastCGI的wrapper(wrapper能夠理解爲用於啓動另外一個程序的程序)

首先須要一個wrapper,這個wrapper須要完成的工做:

經過調用fastcgi(庫)的函數經過socket 與 ningx通訊(讀寫socket是fastcgi內部實現的功能,對wrapper是非透明的)
調度thread,進行fork和kill
和application(php)進行通訊

spawn-fcgi與PHP-FPM
FastCGI接口方式在腳本解析服務器上啓動一個或者多個守護進程對動態腳本進行解析,這些進程就是FastCGI進程管理器,或者稱爲FastCGI引擎。 spawn-fcgi與PHP-FPM就是支持PHP的兩個FastCGI進程管理器。所以HTTPServer徹底解放出來,能夠更好地進行響應和併發處理。
php-fpm比spawn-fcgi更穩定更優秀

使用Nginx+PHP/PHP-FPM這個組合對PHP進行解析。

FastCGI 的主要優勢是把動態語言和HTTP Server分離開來
Nginx與PHP/PHP-FPM常常被部署在不一樣的服務器上,以分擔前端Nginx服務器的壓力,使Nginx專注處理靜態請求和轉發動態請求,而PHP/PHP-FPM服務器專注解析PHP動態請求。

Nginx+PHP-FPM

PHP5.3.3已經集成php-fpm
PHP-FPM提供了更好的PHP進程管理方式
./configure的時候帶 –enable-fpm參數便可開啓PHP-FPM。

安裝Nginx和PHP-FPM完後,配置信息:

PHP-FPM的默認配置php-fpm.conf:
listen_address 127.0.0.1:9000 #這個表示php的fastcgi進程監聽的ip地址以及端口
start_servers
min_spare_servers
max_spare_servers

Nginx配置運行php: 編輯nginx.conf加入以下語句:
location ~ .php$ {
root html;
fastcgi_pass 127.0.0.1:9000; #指定了fastcgi進程偵聽的端口,nginx就是經過這裏與php交互的
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;
}

Nginx經過location指令,將全部以php爲後綴的文件都交給127.0.0.1:9000來處理,而這裏的IP地址和端口就是FastCGI進程監聽的IP地址和端口。

其總體工做流程:
1)、FastCGI進程管理器php-fpm自身初始化,啓動主進程php-fpm和啓動start_servers個CGI 子進程。
主進程php-fpm主要是管理fastcgi子進程,監聽9000端口。
fastcgi子進程等待來自Web Server的鏈接。
2)、當客戶端請求到達Web Server Nginx是時,Nginx經過location指令,將全部以php爲後綴的文件都交給127.0.0.1:9000來處理,即Nginx經過location指令,將全部以php爲後綴的文件都交給127.0.0.1:9000來處理。
3)FastCGI進程管理器PHP-FPM選擇並鏈接到一個子進程CGI解釋器。Web server將CGI環境變量和標準輸入發送到FastCGI子進程。
4)、FastCGI子進程完成處理後將標準輸出和錯誤信息從同一鏈接返回Web Server。當FastCGI子進程關閉鏈接時,請求便告處理完成。
5)、FastCGI子進程接着等待並處理來自FastCGI進程管理器(運行在 WebServer中)的下一個鏈接。

Nginx+PHP正確配置

通常web都作統一入口:把PHP請求都發送到同一個文件上,而後在此文件裏經過解析「REQUEST_URI」實現路由。

Nginx配置文件分爲好多塊,常見的從外到內依次是「http」、「server」、「location」等等,缺省的繼承關係是從外到內,也就是說內層塊會自動獲取外層塊的值做爲缺省值。

server {
    listen 80;
    server_name foo.com;
    root /path;
    location / {
        index index.html index.htm index.php;
        if (!-e $request_filename) {
            rewrite . /index.php last;
        }
    }
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /path$fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
    }
}

1) 不該該在location
模塊定義index
一旦將來須要加入新的「location」,必然會出現重複定義的「index」指令,這是由於多個「location」是平級的關係,不存在繼承,此時應該在「server」裏定義「index」,藉助繼承關係,「index」指令在全部的「location」中都能生效。

不少人喜歡用「if」指令作一系列的檢查,不過這其實是「try_files」指令的職責:

try_files $uri $uri/ /index.php;

3)fastcgi_params 配置文件:

include fastcgi_params;

Nginx有兩份fastcgi配置文件,分別是「fastcgi_params」和「fastcgi.conf」,它們沒有太大的差別,惟一的區別是後者比前者多了一行「SCRIPT_FILENAME」的定義:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

注意:$document_root 和 $fastcgi_script_name 之間沒有 /。

改良後的版本,是否是比開始的版本清爽了不少:

server {  
    listen 80;  
    server_name foo.com;  
    root /path;  
    index index.html index.htm index.php;  
    location / {  
        try_files $uri $uri/ /index.php;  
    }  
    location ~ \.php$ {  
       try_files $uri =404;  
       include fastcgi.conf;  
       fastcgi_pass 127.0.0.1:9000;  
   }  
}

Nginx爲啥性能高-多進程IO模型

nginx採用多進程模型好處

nginx多進程事件模型:異步非阻塞

進程帶來的內存佔用很是大,進程的上下文切換帶來的cpu開銷很大,天然性能就上不去了,而這些開銷徹底是沒有意義的。

nginx(master進程)-----> 多個nginx work 進程 ------> 處理 http 請求

非阻塞就是,事件沒有準備好,立刻返回EAGAIN,告訴你,事件還沒準備好呢,你慌什麼,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準備好了爲止,在這期間,你就能夠先去作其它事情,而後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你能夠作更多的事情了,但帶來的開銷也是不小的。

Nginx優化

1. 編譯安裝過程優化

1).減少Nginx編譯後的文件大小

在編譯Nginx時,默認以debug模式進行,而在debug模式下會插入不少跟蹤和ASSERT之類的信息,編譯完成後,一個Nginx要有好幾兆字節。而在編譯前取消Nginx的debug模式,編譯完成後Nginx只有幾百千字節。所以能夠在編譯以前,修改相關源碼,取消debug模式。具體方法以下:

在Nginx源碼文件被解壓後,找到源碼目錄下的auto/cc/gcc文件,在其中找到以下幾行:

# debug
CFLAGS=」$CFLAGS -g」
註釋掉或刪掉這兩行,便可取消debug模式。

2.爲特定的CPU指定CPU類型編譯優化

在編譯Nginx時,默認的GCC編譯參數是「-O」,要優化GCC編譯,可使用如下兩個參數:

--with-cc-opt='-O3'
--with-cpu-opt=CPU #爲特定的 CPU 編譯,有效的值包括:
pentium, pentiumpro, pentium3, # pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64

要肯定CPU類型,能夠經過以下命令:

cat /proc/cpuinfo | grep "model name"

  1. 利用TCMalloc優化Nginx的性能

與標準的glibc庫的Malloc相比,TCMalloc庫在內存分配效率和速度上要高不少

要安裝TCMalloc庫,須要安裝libunwind(32位操做系統不須要安裝)和google-perftools兩個軟件包,libunwind庫爲基於64位CPU和操做系統的程序提供了基本函數調用鏈和函數調用寄存器功能。下面介紹利用TCMalloc優化Nginx的具體操做過程。

1).安裝libunwind庫

能夠從http://download.savannah.gnu.org/releases/libunwind下載相應的libunwind版本,這裏下載的是libunwind-0.99-alpha.tar.gz。安裝過程以下:

tar zxvf libunwind-0.99-alpha.tar.gz  
cd libunwind-0.99-alpha/  
CFLAGS=-fPIC ./configure  
make CFLAGS=-fPIC  
make CFLAGS=-fPIC install

2).安裝google-perftools

能夠從http://google-perftools.googlecode.com下載相應的google-perftools版本,這裏下載的是google-perftools-1.8.tar.gz。安裝過程以下:

tar zxvf google-perftools-1.8.tar.gz  
cd google-perftools-1.8/  
 ./configure  
make && make install  
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf  
ldconfig

3).從新編譯Nginx

爲了使Nginx支持google-perftools,須要在安裝過程當中添加「–with-google_perftools_module」選項從新編譯Nginx。安裝代碼以下:

./configure \  
--with-google_perftools_module \
--with-http_stub_status_module  \
--prefix=/opt/nginx  \

make  
make install

4).爲google-perftools添加線程目錄

建立一個線程目錄,這裏將文件放在/tmp/tcmalloc下。操做以下:

mkdir /tmp/tcmalloc  
chmod 0777 /tmp/tcmalloc

5).修改Nginx主配置文件

修改nginx.conf文件,在pid這行的下面添加以下代碼:

#pid        logs/nginx.pid;  
google_perftools_profiles /tmp/tcmalloc; 

lsof -n | grep tcmalloc

Nginx配置文件中設置worker_processes的值爲4,所以開啓了4個Nginx線程,每一個線程會有一行記錄。每一個線程文件後面的數字值就是啓動的Nginx的pid值。

3.Nginx內核參數優化

內核參數的優化,主要是在Linux系統中針對Nginx應用而進行的系統內核參數優化。

下面給出一個優化實例以供參考。

net.ipv4.tcp_max_tw_buckets = 6000 
net.ipv4.ip_local_port_range = 1024 65000  
net.ipv4.tcp_tw_recycle = 1 
net.ipv4.tcp_tw_reuse = 1 
net.ipv4.tcp_syncookies = 1 
net.core.somaxconn = 262144 
net.core.netdev_max_backlog = 262144 
net.ipv4.tcp_max_orphans = 262144 
net.ipv4.tcp_max_syn_backlog = 262144 
net.ipv4.tcp_synack_retries = 1 
net.ipv4.tcp_syn_retries = 1 
net.ipv4.tcp_fin_timeout = 1 
net.ipv4.tcp_keepalive_time = 30

將上面的內核參數值加入/etc/sysctl.conf文件中,而後執行以下命令使之生效:

/sbin/sysctl -p

TCP參數設置:

net.ipv4.tcp_max_tw_buckets :選項用來設定timewait的數量,默認是180 000,這裏設爲6000。

net.ipv4.ip_local_port_range:選項用來設定容許系統打開的端口範圍。在高併發狀況不然端口號會不夠用。當NGINX充當代理時,每一個到上游服務器的鏈接都使用一個短暫或臨時端口。

net.ipv4.tcp_tw_recycle:選項用於設置啓用timewait快速回收.

net.ipv4.tcp_tw_reuse:選項用於設置開啓重用,容許將TIME-WAIT sockets從新用於新的TCP鏈接。

net.ipv4.tcp_syncookies:選項用於設置開啓SYN Cookies,當出現SYN等待隊列溢出時,啓用cookies進行處理。

net.ipv4.tcp_max_orphans:選項用於設定系統中最多有多少個TCP套接字不被關聯到任何一個用戶文件句柄上。若是超過這個數字,孤立鏈接將當即被複位並打印出警告信息。這個限制只是爲了防止簡單的DoS攻擊。不能過度依靠這個限制甚至人爲減少這個值,更多的狀況下應該增長這個值。

net.ipv4.tcp_max_syn_backlog:選項用於記錄那些還沒有收到客戶端確認信息的鏈接請求的最大值。對於有128MB內存的系統而言,此參數的默認值是1024,對小內存的系統則是128。

net.ipv4.tcp_synack_retries參數的值決定了內核放棄鏈接以前發送SYN+ACK包的數量。

net.ipv4.tcp_syn_retries選項表示在內核放棄創建鏈接以前發送SYN包的數量。

net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態的時間。默認值是60秒。正確設置這個值很是重要,有時即便一個負載很小的Web服務器,也會出現大量的死套接字而產生內存溢出的風險。

net.ipv4.tcp_syn_retries選項表示在內核放棄創建鏈接以前發送SYN包的數量。

若是發送端要求關閉套接字,net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態的時間。接收端能夠出錯並永遠不關閉鏈接,甚至意外宕機。

net.ipv4.tcp_fin_timeout的默認值是60秒。須要注意的是,即便一個負載很小的Web服務器,也會出現由於大量的死套接字而產生內存溢出的風險。FIN-WAIT-2的危險性比FIN-WAIT-1要小,由於它最多隻能消耗1.5KB的內存,可是其生存期長些。

net.ipv4.tcp_keepalive_time選項表示當keepalive啓用的時候,TCP發送keepalive消息的頻度。默認值是2(單位是小時)。

緩衝區隊列:
net.core.somaxconn:選項的默認值是128, 這個參數用於調節系統同時發起的tcp鏈接數,在高併發的請求中,默認的值可能會致使連接超時或者重傳,所以,須要結合併發請求數來調節此值。

由NGINX可接受的數目決定。默認值一般很低,但能夠接受,由於NGINX 接收鏈接很是快,但若是網站流量大時,就應該增長這個值。內核日誌中的錯誤消息會提醒這個值過小了,把值改大,直到錯誤提示消失。
注意: 若是設置這個值大於512,相應地也要改變NGINX listen指令的backlog參數。

net.core.netdev_max_backlog:選項表示當每一個網絡接口接收數據包的速率比內核處理這些包的速率快時,容許發送到隊列的數據包的最大數目。

4. PHP-FPM的優化

1)增長FastCGI進程數

把PHP FastCGI子進程數調到100或以上,在4G內存的服務器上200就能夠建議經過壓力測試獲取最佳值。

2)增長 PHP-FPM打開文件描述符的限制

標籤rlimit_files用於設置PHP-FPM對打開文件描述符的限制,默認值爲1024。這個標籤的值必須和Linux內核打開文件數關聯起來,例如,要將此值設置爲65 535,就必須在Linux命令行執行「ulimit -HSn 65536」。

而後 增長 PHP-FPM打開文件描述符的限制:

vim /path/to/php-fpm.conf

rlimit_files = 1024

把1024更改成 4096或者更高.
重啓 PHP-FPM.

命令行下執行 ulimit -n 65536便可修改

設置 /etc/security/limits.conf,加入

  • hard nofile 65536

  • soft nofile 65536

3)適當增長max_requests

標籤max_requests指明瞭每一個children最多處理多少個請求後便會被關閉,默認的設置是500。
max_requests = 500

4.nginx.conf的參數優化

nginx要開啓的進程數 通常等於cpu的總核數 其實通常狀況下開4個或8個就能夠。
每一個nginx進程消耗的內存10兆的模樣

orker_cpu_affinity
僅適用於linux,使用該選項能夠綁定worker進程和CPU(2.4內核的機器用不了

nginx可使用多個worker進程,緣由以下:

use epoll
worker_processes
worker_connections 65535
keepalive_timeout 75
client_header_buffer_size 16k
large_client_header_buffers 4 32k
open_file_cache max 102400
open_file_cache_min_uses

5.訪問日誌
6.限流
limit_conn and limit_conn_zone:NGINX接受客戶鏈接的數量限制,例如單個IP地址的鏈接。設置這些指令能夠防止單個用戶打開太多的鏈接,消耗超出本身的資源。
limit_rate:傳輸到客戶端響應速度的限制(每一個打開多個鏈接的客戶消耗更多的帶寬)。設置這個限制防止系統過載,確保全部客戶端更均勻的服務質量。
limit_req and limit_req_zone:NGINX處理請求的速度限制,與limit_rate有相同的功能。能夠提升安全性,尤爲是對登陸頁面,經過對用戶限制請求速率設置一個合理的值,避免太慢的程序覆蓋你的應用請求(好比DDoS攻擊)。
max_conns:上游配置塊中服務器指令參數。在上游服務器組中單個服務器可接受最大併發數量。使用這個限制防止上游服務器過載。設置值爲0(默認值)表示沒有限制。
queue (NGINX Plus) :建立一個隊列,用來存放在上游服務器中超出他們最大max_cons限制數量的請求。這個指令能夠設置隊列請求的最大值,還能夠選擇設置在錯誤返回以前最大等待時間(默認值是60秒)。若是忽略這個指令,請求不會放入隊列。

7. 錯誤排查

一、Nginx 502 Bad Gateway

php-cgi進程數不夠用、php執行時間長(mysql慢)、或者是php-cgi進程死掉,都會出現502錯誤

通常來講Nginx 502 Bad Gateway和php-fpm.conf的設置有關,而Nginx 504 Gateway Time-out則是與nginx.conf的設置有關

1)、查看當前的PHP FastCGI進程數是否夠用:

netstat -anpo | grep "php-cgi" | wc -l

2)、部分PHP程序的執行時間超過了Nginx的等待時間,能夠適當增長
nginx.conf配置文件中FastCGI的timeout時間,例如:
http {
......
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
......
}

二、413 Request Entity Too Large
解決:增大client_max_body_size
client_max_body_size:指令指定容許客戶端鏈接的最大請求實體大小,它出如今請求頭部的Content-Length字段. 若是請求大於指定的值,客戶端將收到一個"Request Entity Too Large" (413)錯誤. 記住,瀏覽器並不知道怎樣顯示這個錯誤.
php.ini中增大
post_max_size 和upload_max_filesize

3 Ngnix error.log出現:upstream sent too big header while reading response header from upstream錯誤

1)若是是nginx反向代理
proxy是nginx做爲client轉發時使用的,若是header過大,超出了默認的1k,就會引起上述的upstream sent too big header (說白了就是nginx把外部請求給後端server,後端server返回的header 太大nginx處理不過來就致使了。

server {
        listen       80;
        server_name  *.xywy.com ;

        large_client_header_buffers 4 16k;

        location / {
          #添加這3行 
           proxy_buffer_size 64k;
           proxy_buffers   32 32k;
           proxy_busy_buffers_size 128k;

           proxy_set_header Host $host;
           proxy_set_header X-Real-IP       $remote_addr;
           proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

2) 若是是 nginx+PHPcgi
錯誤帶有 upstream: "fastcgi://127.0.0.1:9000"。就該多加:

fastcgi_buffer_size 128k;
fastcgi_buffers 4 128k;

server {
        listen       80;
        server_name  ddd.com;
        index index.html index.htm index.php;
   
        client_header_buffer_size 128k;
        large_client_header_buffers 4 128k;
        proxy_buffer_size 64k;
        proxy_buffers 8 64k;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 128k;

        location / {
          ......
        }
}
相關文章
相關標籤/搜索