1. 程式人生 > 實用技巧 >nginx系列之一:nginx入門

nginx系列之一:nginx入門

一、nginx 功能介紹

Nginx因為它的穩定性、豐富的模組庫、靈活的配置和低系統資源的消耗而聞名.業界一致認為它是Apache2.2+mod_proxy_balancer的輕量級代替者,不僅是因為響應靜態頁面的速度非常快,而且它的模組數量達到Apache的近2/3。對proxy和rewrite模組的支援很徹底,還支援mod_fcgi、ssl、vhosts ,適合用來做mongrel clusters的前端HTTP響應。
nginx和Apache一樣使用模組化設計,nginx模組包括內建模組和第三方模組,其中內建模組中包含主模組和事件模組。

nginx處理請求邏輯圖

二、nginx可以提供的服務

  1. web 服務.
  2. 負載均衡 (反向代理)
  3. web cache(web 快取)

三、nginx 的優點

  1. 高併發。靜態小檔案
  2. 佔用資源少。2萬併發、10個執行緒,記憶體消耗幾百M。
  3. 功能種類比較多。web,cache,proxy。每一個功能都不是特別強。
  4. 支援epoll模型,使得nginx可以支援高併發。
  5. nginx 配合動態服務和Apache有區別。(FASTCGI 介面)
  6. 利用nginx可以對IP限速,可以限制連線數。
  7. 配置簡單,更靈活。

四、nginx應用場合

  1. 靜態伺服器。(圖片,視訊服務)另一個lighttpd。併發幾萬,html,js,css,flv,jpg,gif等。
  2. 動態服務,nginx——fastcgi 的方式執行PHP,jsp。(PHP併發在500-1500,MySQL 併發在300-1500)。
  3. 反向代理,負載均衡。日pv2000W以下,都可以直接用nginx做代理。
  4. 快取服務。類似 SQUID,VARNISH。

五、主流web服務產品對比說明

5.1 Apache-特性

  1. 2.2版本本身穩定強大,據官方說:其2.4版本效能更強。
  2. prefork模式取消了程序建立開銷,效能很高。
  3. 處理動態業務資料時,因關聯到後端的引擎和資料庫,瓶頸不在與Apache本身。
  4. 高併發時消耗系統資源相對多一些。
  5. 基於傳統的select模型。
  6. 擴充套件庫,DSO方法。

5.2 nginx-特性

  1. 基於非同步IO模型,(epoll,kqueue),效能強,能夠支援上萬併發。
  2. 對小檔案支援很好,效能很高(限靜態小檔案1M)。
  3. 程式碼優美,擴充套件庫必須編譯進主程式。
  4. 消耗程式碼資源比較低。
  5. lighttpd(百度貼吧,豆瓣)
  6. 基於非同步IO模式,效能和nginx相近。
  7. 擴充套件庫是SO模式,比nginx要靈活。
    8.通過差距(mod_secdownload)可實現檔案URL地址加密。

5.3 web服務產品效能對比測試

5.3.1 靜態資料效能對比

  1. 處理靜態檔案Apache效能比nginx和lighttpd要差。
  2. nginx在處理小檔案優勢明顯。
  3. 處理靜態小檔案(小於1M),nginx和lighttpd比Apache更有優勢,lighttpd最強。

5.3.2 動態資料效能對比

  1. 處理動態內容三者相差不大,主要取決於PHP和資料庫的壓力。
  2. 當處理動態資料時,三者差距不大,從測試結果看,Apache更有優勢一點。這是因為處理動態資料能力取決於PHP和後端資料的提供服務能力。也就是說瓶頸不在web伺服器上。
  3. 一般PHP引擎支援的併發參考值300-1000,JAVA引擎併發300-1000,資料庫的併發300-1000.

5.3.3 為什麼nginx的總體效能比Apache高。

  1. nginx使用最新的epoll和kqueue網路IO模型,而Apache使用床頭的select模式。
  2. 目前Linux下能夠承受高併發訪問的squid、Memcached 都採用的是epoll網路IO模型。

5.3.4 如何選擇WEB伺服器:

靜態業務:高併發、採用nginx,lighttpd,根據自己的掌握程度或公司的要求。
動態業務:採用nginx和Apache均可。
既有靜態業務又有動態業務:nginx或Apache,不要多選要單選。
動態業務可以由前端代理(haproxy),根據頁面元素的型別,向後轉發相應的伺服器進行處理。
思想:我們工作都不要追求一步到位,滿足需求的前提下,先用,然後逐步完善。
提示:nginx做web(Apache,lighttpd)、反向代理(haproxy,lvs,nat)及快取伺服器(squid)也是不錯的。
最終建議:對外的業務nginx,對內的業務Apache(yum httpd mysql-server php)。

六、nginx實戰過程

6.1 安裝依賴包

  • nginx安裝依賴GCC、openssl-devel、pcre-devel和zlib-devel軟體庫。
  • Pcre全稱(Perl Compatible Regular Expressions),中文perl相容正則表示式,pcre官方站點
yum install  pcre pcre-devel -y 
yum install openssl openssl-devel -y 

6.2 開始編譯

使用./configure --help檢視各個模組的使用情況,使用--without-http_ssi_module的方式關閉不需要的模組。可以使用--with-http_perl_modules方式安裝需要的模組。

tar -zxf nginx-1.10.1.tar.gz 
cd nginx-1.10.1/
./configure --prefix=/data/nginx-1.10.1 --user=nginx --group=nginx  --with-http_ssl_module  --with-http_stub_status_module

useradd nginx -M -s /sbin/nologin 
make && make install 
ln -s /data/nginx-1.10.1 /data/nginx

6.2.1 編譯命令


6.2.2 測試nginx配置檔案是否正常

/data/nginx/sbin/nginx -t 
nginx: the configuration file /data/nginx-1.10.1/conf/nginx.conf syntax is ok
nginx: configuration file /data/nginx-1.10.1/conf/nginx.conf test is successful

6.2.3 啟動nginx伺服器

/data/nginx/sbin/nginx  -t  ##檢查配置檔案
/data/nginx/sbin/nginx      ##確定nginx服務
netstat -lntup |grep nginx      ## 檢查程序是否正常
curl http://localhost           ## 確認結果

6.2.4 nginx其他命令

nginx -s signal
signal:
stop — fast shutdown
quit — graceful shutdown
reload — reloading the configuration file
reopen — reopening the log files
用來開啟日誌檔案,這樣nginx會把新日誌資訊寫入這個新的檔案中

/data/nginx/sbin/nginx -V檢視已經編譯的引數。

使用kill命令操作nginx。格式:kill -訊號 PID

訊號名稱

  • TERM,INT 快速關閉
  • QUIT 優雅的關閉,保持吸納有的客戶端連線
  • HUP 重啟應用新的配置檔案
  • USR1 重新開啟日誌檔案
  • USR2 升級程式
  • WINCH 優雅的關閉工作程序

例子

kill -QUIT  `cat /data/nginx/nginx.pid`
kill -HUP `cat /data/nginx/nginx.pid`

七、nginx配置檔案

配置基礎配置檔案

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}


### 測試配置檔案是否正常
shell> /data/nginx/sbin/nginx -t 
nginx: the configuration file /data/nginx-1.10.3/conf/nginx.conf syntax is ok
nginx: configuration file /data/nginx-1.10.3/conf/nginx.conf test is successful
shell> curl -I http://localhost
HTTP/1.1 200 OK

八、nginx監控

開啟nginx的監控服務

8.1 開啟狀態頁

#設定檢視Nginx狀態的地址   
location /status {  
  stub_status on;       #表示開啟stubStatus的工作狀態統計功能。
  access_log off;       #access_log off; 關閉access_log 日誌記錄功能。
  #auth_basic "status";                               #auth_basic 是nginx的一種認證機制。
  #auth_basic_user_file conf/htpasswd;    #用來指定密碼檔案的位置。
}

8.2 配置登入密碼

yum install -y httpd-tools
/usr/local/apache/bin/htpasswd -c /data/nginx/conf/htpasswd biglittleant 
New password:

完成後會在/data/nginx/conf/目錄下生成htpasswd檔案。

8.3 訪問URL

curl http://127.0.0.1/status

Active connections:  1
server accepts handled requests
 16 16 18
Reading: 0 Writing: 1 Waiting: 0

#active connections – 活躍的連線數量
#server accepts handled requests — 總共處理了16個連線 , 成功建立16次握手, 總共處理了18個請求
#Reading — 讀取客戶端的連線數: Writing 響應資料到客戶端的數量; Waiting 開啟 keep-alive 的情況下,這個值等於 active – (reading+writing), 意思就是 Nginx 已經處理完正在等候下一次請求指令的駐留連線.

8.3 編寫zabbix監控指令碼

nginx_status_fun(){
    NGINX_PORT=$1
    NGINX_COMMAND=$2
    nginx_active(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| grep 'Active' | awk '{print $NF}'
        }
    nginx_reading(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| grep 'Reading' | awk '{print $2}'
       }
    nginx_writing(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| grep 'Writing' | awk '{print $4}'
       }
    nginx_waiting(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| grep 'Waiting' | awk '{print $6}'
       }
    nginx_accepts(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| awk NR==3 | awk '{print $1}'
       }
    nginx_handled(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| awk NR==3 | awk '{print $2}'
       }
    nginx_requests(){
        /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/status/" 2>/dev/null| awk NR==3 | awk '{print $3}'
       }
    case $NGINX_COMMAND in
        active)
            nginx_active;
            ;;
        reading)
            nginx_reading;
            ;;
        writing)
            nginx_writing;
            ;;
        waiting)
            nginx_waiting;
            ;;
        accepts)
            nginx_accepts;
            ;;
        handled)
            nginx_handled;
            ;;
        requests)
            nginx_requests;
        esac 
}

九、nginx優化

9.1 nginx核心優化

net.ipv4.tcp_fin_timeout = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.ip_local_port_range = 4000    65000
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_orphans = 16384
#以下引數是對iptables防火牆的優化,防火牆不開會提示,可以忽略不理。
net.ipv4.ip_conntrack_max = 25000000
net.ipv4.netfilter.ip_conntrack_max=25000000
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=180
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120

十、擴充套件一:nginx全域性變數

  • $args:這個變數等於請求行中的引數,同$query_string。
  • $is_args: 如果已經設定$args,則該變數的值為"?",否則為""。
  • $content_length: 請求頭中的Content-length欄位。
  • $content_type: 請求頭中的Content-Type欄位。
  • $document_uri: 與$uri相同。
  • $document_root: 當前請求在root指令中指定的值。
  • $host: 請求主機頭欄位,否則為伺服器名稱。
  • $http_user_agent: 客戶端agent資訊。
  • $http_cookie: 客戶端cookie資訊。
  • $limit_rate: 這個變數可以限制連線速率。
  • $request_method: 客戶端請求的動作,通常為GET或POST。
  • $remote_addr: 客戶端的IP地址。
  • $remote_port: 客戶端的埠。
  • $remote_user: 已經經過Auth Basic Module驗證的使用者名稱。
  • $request_body_file`: 客戶端請求主體的臨時檔名。
  • $request_uri: 請求的URI,帶引數
  • $request_filename: 當前請求的檔案路徑,由root或alias指令與URI請求生成。
  • $scheme: 所用的協議,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
  • $server_protocol: 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr: 伺服器地址,在完成一次系統呼叫後可以確定這個值。
  • $server_name: 伺服器名稱。
  • $server_port: 請求到達伺服器的埠號。
  • $request_uri: 包含請求引數的原始URI,不包含主機名,如:/foo/bar.php?arg=baz,它無法修改。
  • $uri: 不帶請求引數的當前URI,$uri不包含主機名,如/foo/bar.html可能和最初的值有不同,比如經過重定向之類的。它可以通過內部重定向,或者使用index指令進行修改。不包括協議和主機名,例如/foo/bar.html。

例子:

訪問連結是:http://localhost:88/test1/test.php 
網站路徑是:/var/www/html

$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test.php
$document_uri:/test1/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test.php

ginx plus – ngx_http_status_module

商業版的 nginx plus 通過他的 ngx_http_status_module 提供了比 nginx 更多的監控指標,可以參看http://demo.nginx.com/status.html

nginx access log 分析

nginx 的 access log 中可以記錄很多有價值的資訊,通過分析 access log,可以收集到很多指標。
python 編寫的 linux 工具 ngxtop 就實現了對 access log 的分析功能。

NDK – ngx_devel_kit

NDK 是一個拓展nginx伺服器核心功能的模組,第三方模組開發可以基於它來快速實現。NDK提供函式和巨集處理一些基本任務,減輕第三方模組開發的程式碼量。

nginx lua – lua-nginx-module

nginx的lua模組,通過這個模組,可以對nginx做定製開發

十、擴充套件二:web伺服器事件處理模型

select

select最早於1983年出現在4.2BSD中,它通過一個select()系統呼叫來監視多個檔案描述符的陣列,當select()返回後,該陣列中就緒的檔案描述符便會被核心修改標誌位,使得程序可以獲得這些檔案描述符從而進行後續的讀寫操作。
select目前幾乎在所有的平臺上支援,其良好跨平臺支援也是它的一個優點,事實上從現在看來,這也是它所剩不多的優點之一。
select的一個缺點在於單個程序能夠監視的檔案描述符的數量存在最大限制,在Linux上一般為1024,不過可以通過修改巨集定義甚至重新編譯核心的方式提升這一限制。
另外,select()所維護的儲存大量檔案描述符的資料結構,隨著檔案描述符數量的增大,其複製的開銷也線性增長。同時,由於網路響應時間的延遲使得大量TCP連線處於非活躍狀態,但呼叫select()會對所有socket進行一次線性掃描,所以這也浪費了一定的開銷。

poll

poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差別,但是poll沒有最大檔案描述符數量的限制。
poll和select同樣存在一個缺點就是,包含大量檔案描述符的陣列被整體複製於使用者態和核心的地址空間之間,而不論這些檔案描述符是否就緒,它的開銷隨著檔案描述符數量的增加而線性增大。
另外,select()和poll()將就緒的檔案描述符告訴程序後,如果程序沒有對其進行IO操作,那麼下次呼叫select()和poll()的時候將再次報告這些檔案描述符,所以它們一般不會丟失就緒的訊息,這種方式稱為水平觸發(Level Triggered)。

epoll

直到Linux2.6才出現了由核心直接支援的實現方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下效能最好的多路I/O就緒通知方法。
epoll可以同時支援水平觸發和邊緣觸發(Edge Triggered,只告訴程序哪些檔案描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有采取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發),理論上邊緣觸發的效能要更高一些,但是程式碼實現相當複雜。
epoll同樣只告知那些就緒的檔案描述符,而且當我們呼叫epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體對映(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統呼叫時複製的開銷。
另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()時便得到通知。

nginx -s reload 過程

nginx主程序讀取配置檔案,如果發現配置檔案變更,會建立一個新的主程序,然後同時舊的程序,及舊的子程序關閉,舊程序會拒絕新的連線,服務到自己的連線結束,然後關閉。

Apache select模型和 nginx epoll 模型對比講解

Nginx的高併發得益於其採用了epoll模型,與傳統的伺服器程式架構不同,epoll是linux核心2.6以後才出現的。下面通過比較Apache和Nginx工作原理來比較。

傳統Apache都是多程序或者多執行緒來工作,假設是多程序工作(prefork),apache會先生成幾個程序,類似程序池的工作原理,只不過這裡的程序池會隨著請求數目的增加而增加。對於每一個連線,apache都是在一個程序內處理完畢。具體是 recv(),以及根據 URI 去進行磁碟I/O來尋找檔案,還有 send()都是阻塞的。其實說白了都是 apche 對於套接字的I/O,讀或者寫,但是讀或者寫都是阻塞的,阻塞意味著程序就得掛起進入sleep狀態,那麼一旦連線數很多,Apache必然要生成更多的程序來響應請求,一旦程序多了,CPU對於程序的切換就頻繁了,很耗資源和時間,所以就導致apache效能下降了,說白了就是處理不過來這麼多程序了。其實仔細想想,如果對於程序每個請求都沒有阻塞,那麼效率肯定會提高很多。

Nginx採用epoll模型,非同步非阻塞。對於Nginx來說,把一個完整的連線請求處理都劃分成了事件,一個一個的事件。比如accept(), recv(),磁碟I/O,send()等,每部分都有相應的模組去處理,一個完整的請求可能是由幾百個模組去處理。真正核心的就是事件收集和分發模組,這就是管理所有模組的核心。只有核心模組的排程才能讓對應的模組佔用CPU資源,從而處理請求。拿一個HTTP請求來說,首先在事件收集分發模組註冊感興趣的監聽事件,註冊好之後不阻塞直接返回,接下來就不需要再管了,等待有連線來了核心會通知你(epoll的輪詢會告訴程序),cpu就可以處理其他事情去了。一旦有請求來,那麼對整個請求分配相應的上下文(其實已經預先分配好),這時候再註冊新的感興趣的事件(read函式),同樣客戶端資料來了核心會自動通知程序可以去讀資料了,讀了資料之後就是解析,解析完後去磁碟找資源(I/O),一旦I/O完成會通知程序,程序開始給客戶端發回資料send(),這時候也不是阻塞的,呼叫後就等核心發回通知傳送的結果就行。整個下來把一個請求分成了很多個階段,每個階段都到很多模組去註冊,然後處理,都是非同步非阻塞。非同步這裡指的就是做一個事情,不需要等返回結果,做好了會自動通知你。

select/epoll的特點

select的特點:select 選擇控制代碼的時候,是遍歷所有控制代碼,也就是說控制代碼有事件響應時,select需要遍歷所有控制代碼才能獲取到哪些控制代碼有事件通知,因此效率是非常低。但是如果連線很少的情況下, select和epoll的LT觸發模式相比, 效能上差別不大。
這裡要多說一句,select支援的控制代碼數是有限制的, 同時只支援1024個,這個是控制代碼集合限制的,如果超過這個限制,很可能導致溢位,而且非常不容易發現問題, 當然可以通過修改linux的socket核心調整這個引數。
epoll的特點:epoll對於控制代碼事件的選擇不是遍歷的,是事件響應的,就是控制代碼上事件來就馬上選擇出來,不需要遍歷整個控制代碼連結串列,因此效率非常高,核心將控制代碼用紅黑樹儲存的。
對於epoll而言還有ET和LT的區別,LT表示水平觸發,ET表示邊緣觸發,兩者在效能以及程式碼實現上差別也是非常大的。

不管是Nginx還是Squid這種反向代理,其網路模式都是事件驅動。事件驅動其實是很老的技術,早期的select、poll都是如此。後來基於核心通知的更高階事件機制出現,如libevent裡的epoll,使事件驅動效能得以提高。事件驅動的本質還是IO事件,應用程式在多個IO控制代碼間快速切換,實現所謂的非同步IO。事件驅動伺服器,最適合做的就是這種IO密集型工作,如反向代理,它在客戶端與WEB伺服器之間起一個數據中轉作用,純粹是IO操作,自身並不涉及到複雜計算。反向代理用事件驅動來做,顯然更好,一個工作程序就可以run了,沒有程序、執行緒管理的開銷,CPU、記憶體消耗都小。

所以Nginx、Squid都是這樣做的。當然,Nginx也可以是多程序 + 事件驅動的模式,幾個程序跑libevent,不需要Apache那樣動輒數百的程序數。Nginx處理靜態檔案效果也很好,那是因為靜態檔案本身也是磁碟IO操作,處理過程一樣。至於說多少萬的併發連線,這個毫無意義。隨手寫個網路程式都能處理幾萬的併發,但如果大部分客戶端阻塞在那裡,就沒什麼價值。

再看看Apache或者Resin這類應用伺服器,之所以稱他們為應用伺服器,是因為他們真的要跑具體的業務應用,如科學計算、圖形影象、資料庫讀寫等。它們很可能是CPU密集型的服務,事件驅動並不合適。例如一個計算耗時2秒,那麼這2秒就是完全阻塞的,什麼event都沒用。想想MySQL如果改成事件驅動會怎麼樣,一個大型的join或sort就會阻塞住所有客戶端。這個時候多程序或執行緒就體現出優勢,每個程序各幹各的事,互不阻塞和干擾。當然,現代CPU越來越快,單個計算阻塞的時間可能很小,但只要有阻塞,事件程式設計就毫無優勢。所以程序、執行緒這類技術,並不會消失,而是與事件機制相輔相成,長期存在。

總言之,事件驅動適合於IO密集型服務,多程序或執行緒適合於CPU密集型服務,它們各有各的優勢,並不存在誰取代誰的傾向。

轉載:https://blog.csdn.net/qq_29677867/article/details/90112120