一、nginx高效的原理:
1、概念:
NGINX采用了異步、事件驅動的方法來處理連接。這種處理方式無需(像使用傳統架構的服務器一樣)為每個請求創建額外的專用進程或者線程,而是在一個工作進程中處理多個連接和請求。為此,NGINX工作在非阻塞的socket模式下,並使用了epoll 和 kqueue這樣有效的方法。
2、什么是epoll
epoll的最大好處在於他不會隨着被監控描述符的數目的增長而導致效率極致下降。epoll監控的描述符的數目很大,並且epoll對描述符的響應是觸發的,即當有描述符有時間發生會有觸發。
3、什么是文件描述符
文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞着文件描述符展開。
4、epoll的工作
檢測四類事件:
# 處理新事件
# 定時處理事件
# 處理普通讀寫
# 處理從磁盤讀事件
(1)nginx的處理連接事件和處理其他事件都是在同一個I/O復用下,那么它是如何保證連接事件對響應的要求的呢?niginx是通過將獲取的事件先不調用其回調,而是把他們先放入倆個post隊列,這倆個隊列分別為
.ngx_posted_accept_events
.ngx_posted_events
第一個隊列用來保存連接事件,而第二個隊列用來保存普通讀寫事件,之后在執行時我們可以先保證ngx_posted_accept_events中的事件先處理,就可以保證連接對響應速度的敏感性
(2)如何防止串話
串話問題可以說是服務器程序中都需要處理的一個問題。串話問題是指剛剛關閉了一個套接字,又來了一個新連接,而新連接剛好系統給分配的就是剛關閉的那個套接字,那么如果方才哪個套接字還有事件未處理完成,接下來它給對應的套接字發送數據很有可能就會發到新建立的用戶那。那么nginx如何來解決這個問題呢?很簡單,nginx在每次獲得新連接后都會將連接中的一個標志為置反,這樣本個連接和上個連接的instance就會不同,而每個事件都包含了連接,所以每次處理事件時只需要比較事件中的instance是否相同就OK了
(3)如何處理”驚群問題”
所謂驚群問題就是說多個進程在同時監聽同一個端口,當有連接到來時,系統會把多個進程都喚醒,但是當然任然只有一個進程能處理到新連接,所以本來其他進程是不需要被喚醒的,但是被喚醒了,這就是注明的驚群問題。nginx解決它的方法也很簡單,只需要保證同一時間點只有一個進程在監聽端口就可避免驚群問題了。但是問題的關鍵是如何能保證同一時間點只有一個進程來監聽端口。nginx采用了嘗試加鎖,根據加鎖的返回值確定本進程是否要接下來處理新連接事件,從而解決了”驚群問題”
(4)如何解決負載均衡
nginx解決個進程間的負載均衡問題並沒有均衡分配,而是當每個進程處理的額連接數超過了規定其處理的最大連接數的7/8時,就會本次不處理連接,而是將其處理的連接數-1,這樣相當於就把機會讓給了其他線程,從而實現了負載均衡了。
5、i/o復用
1、master進程
主要用來管理worker進程,包含:接收來自外界的信號,向各worker進程發送信號,監控worker進程的運行狀態,當worker進程退出后(異常情況下),會自動重新啟動新的worker進程。master進程充當整個進程組與用戶的交互接口,同時對進程進行監護。它不需要處理網絡事件,不負責業務的執行,只會通過管理worker進程來實現重啟服務、平滑升級、更換日志文件、配置文件實時生效等功能
2、worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。當我們提供80端口的http服務時,一個連接請求過來,每個進程都有可能處理這個連接,怎么做到的呢?首先,每個worker進程都是從master進程fork過來,在master進程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多個worker進程。所有worker進程的listenfd會在新連接到來時變得可讀,為保證只有一個進程處理該連接,所有worker進程在注冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程注冊listenfd讀事件,在讀事件里調用accept接受該連接。當一個worker進程在accept這個連接之后,就開始讀取請求,解析請求,處理請求,產生數據后,再返回給客戶端,最后才斷開連接,這樣一個完整的請求就是這樣的了。worker_processes 8;
nginx進程數,建議按照cpu數目來指定,一般為它的倍數。
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
為每個進程分配cpu,上例中將8個進程分配到8個cpu,當然可以寫多個,或者將一個進程分配到多個cpu。
worker_rlimit_nofile 102400;
這個指令是指當一個nginx進程打開的最多文件描述符數目,理論值應該是最多打開文件數(ulimit -n)與nginx進程數相除,但是nginx分配請求並不是那么均勻,所以最好與ulimit -n的值保持一致。
2、Events模塊
events模塊中包含nginx中所有處理連接的設置。
events {
worker_connections 65536;
multi_accept on;
use epoll;
}
worker_connections 設置可由一個worker進程同時打開的最大連接數。如果設置了上面提到的worker_rlimit_nofile,我們可以將這個值設得很高。記住,最大客戶數也由系統的可用socket連接數限制(~ 64K),所以設置不切實際的高沒什么好處。
multi_accept 告訴nginx收到一個新連接通知后接受盡可能多的連接。
use 設置用於復用客戶端線程的輪詢方法。如果你使用Linux 2.6+,你應該使用epoll。如果你使用*BSD,你應該使用kqueue。
3、http模塊
HTTP模塊控制着nginx http處理的所有核心特性。因為這里只有很少的配置,所以我們只節選配置的一小部分。所有這些設置都應該在http模塊中,甚至你不會特別的注意到這段設置。
http {
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens 並不會讓nginx執行的速度更快,但它可以關閉在錯誤頁面中的nginx版本數字,這樣對於安全性是有好處的。
sendfile 可以讓sendfile()發揮作用。sendfile()可以在磁盤和TCP socket之間互相拷貝數據(或任意兩個文件描述符)。Pre-sendfile是傳送數據之前在用戶空間申請數據緩沖區。之后用read()將數據從文件拷貝到這個緩沖區,write()將緩沖區數據寫入網絡。sendfile()是立即將數據從磁盤讀到OS緩存。因為這種拷貝是在內核完成的, sendfile()要比組合read()和write()以及打開關閉丟棄緩沖更加有效(更多有關於sendfile)。
tcp_nopush 告訴nginx在一個數據包里發送所有頭文件,而不一個接一個的發送。
tcp_nodelay 告訴nginx不要緩存數據,而是一段一段的發送--當需要及時發送數據時,就應該給應用設置這個屬性,這樣發送一小塊數據信息時就不能立即得到返回值。
client_header_buffer_size 4k;
客戶端請求頭部的緩沖區大小,這個可以根據你的系統分頁大小來設置,一般一個請求的頭部大小不會超過1k,不過由於一般系統分頁都要大於1k,所以這里設置為分頁大小。分頁大小可以用命令getconf PAGESIZE取得。
open_file_cache max=102400 inactive=20s;
這個將為打開文件指定緩存,默認是沒有啟用的,max指定緩存數量,建議和打開文件數一致,inactive是指經過多長時間文件沒被請求后刪除緩存。
open_file_cache_valid 30s;
這個是指多長時間檢查一次緩存的有效信息。
open_file_cache_min_uses 1;
open_file_cache指令中的inactive參數時間內文件的最少使用次數,如果超過這個數字,文件描述符一直是在緩存中打開的,如上例,如果有一個文件在inactive時間內一次沒被使用,它將被移除。
keepalive_timeout 10;
client_header_timeout 10;
client_body_timeout 10;
reset_timedout_connection on;
send_timeout 10;
keepalive_timeout 給客戶端分配keep-alive鏈接超時時間。服務器將在這個超時時間過后關閉鏈接。我們將它設置低些可以讓ngnix持續工作的時間更長。
client_header_timeout 和client_body_timeout 設置請求頭和請求體(各自)的超時時間。我們也可以把這個設置低些。
reset_timeout_connection 告訴nginx關閉不響應的客戶端連接。這將會釋放那個客戶端所占有的內存空間。
send_timeout 指定客戶端的響應超時時間。這個設置不會用於整個轉發器,而是在兩次客戶端讀取操作之間。如果在這段時間內,客戶端沒有讀取任何數據,nginx就會關閉連接。
open_file_cache max=100000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
open_file_cache 打開緩存的同時也指定了緩存最大數目,以及緩存的時間。我們可以設置一個相對高的最大時間,這樣我們可以在它們不活動超過20秒后清除掉。
open_file_cache_valid 在open_file_cache中指定檢測正確信息的間隔時間。
open_file_cache_min_uses 定義了open_file_cache中指令參數不活動時間期間里最小的文件數。
open_file_cache_errors 指定了當搜索一個文件時是否緩存錯誤信息,也包括再次給配置中添加文件。我們也包括了服務器模塊,這些是在不同文件中定義的。如果你的服務器模塊不在這些位置,你就得修改這一行來指定正確的位置。
client_header_timeout 和client_body_timeout 設置請求頭和請求體(各自)的超時時間。我們也可以把這個設置低些。
reset_timeout_connection 告訴nginx關閉不響應的客戶端連接。這將會釋放那個客戶端所占有的內存空間。
send_timeout 指定客戶端的響應超時時間。這個設置不會用於整個轉發器,而是在兩次客戶端讀取操作之間。如果在這段時間內,客戶端沒有讀取任何數據,nginx就會關閉連接。
limit_conn_zone $binary_remote_addr zone=addr:5m;
limit_conn addr 100;
limit_conn_zone 設置用於保存各種key(比如當前連接數)的共享內存的參數。5m就是5兆字節,這個值應該被設置的足夠大以存儲(32K*5)32byte狀態或者(16K*5)64byte狀態。
limit_conn 為給定的key設置最大連接數。這里key是addr,我們設置的值是100,也就是說我們允許每一個IP地址最多同時打開有100個連接。
