一、簡要回顧
IO模型:
blocking、nonblocking、multiplexing、event-driven、AIO
Nginx特性:non-blocking、event-driven、AIO
所以Nginx在典型的這幾項IO模型當中的所謂實現高並發服務器端編程的概念上都是支持的。
Nginx其此版本號為奇數的通常為開發版,為偶數的通常為穩定版,當然作為我們來講,通常在生產環境中能夠用到的應該是stable版本的。
Nginx的開發也遵循這樣的一種特性,一旦它的某一個偶數版升級以后那么隨之而來的比它大一個數字的奇數版將成為新的開發板,這是需要注意的一點。在1.8.0當中,他已經引進了incorporating許多新特性包括幾個著名的新特性。
二、Nginx1.8所引入支持的新特性(要做到了解)
- hash load balancing method(基於hash計算的負載均衡方法)
- backed SSL certificate verification(支持后端SSL證書校驗)
- thread pools support(支持線程池)
以后要定義一個對應的Nginx線程,能支持多少個並發連接,可以通過線程池定義,這是一個很強大的也是很新的特性。
- proxy request buffering(代理請求的緩沖)
那這樣一來新特性的引入勢必能夠更多的更強大的對於所謂代理模式下的連接的管理功能。
二、Nginx常用指令介紹
1、主配置段的指令
- 正常運行的必備配置:
user USERNAME [GROUPNAME];
指定運行worker進程的用戶和組,組身份身份可省。它的組身份與用戶身份是統一使用同一個身份來定義的,這一點有別於httpd。如果在編譯時已經使用--user,--group指定了的話,那在配置文件中它很有可能是被注釋掉的,因為它默認就會使用那一個對應的用戶名來進行定義了。
例:user nginx nginx;
注意:每一個指令必須使用分號結尾否則視為語法錯誤。
pid /path/to/pid_file;
指定Nginx守護進程的pid文件的位置;如果在編譯時已指定在配置文件中可能被注釋掉表明使用編譯時指定的默認路徑;
例:pid /var/run/nginx/nginx.pid;
大多數守護進程都會有pid文件來保存其pid信息的,包括此前學的httpd,甚至於是以fpm模式工作的php,它們都有自己的pid文件;
worker_rlimit_nofile number;
指定所有worker進程(加起來)所能夠打開的最大文件句柄數;說白了就是所能夠打開的最大文件數量。因為worker進程都是以Nginx用戶的身份運行的,所以是一共能夠打開的文件的總數。
Linux系統上包括管理員默認都受限於所能夠運行的資源的數量。每個用戶默認能夠打開的文件數是1024個,但是對於任何一個服務器軟件來講,如果它要監聽在某個套接字上提供服務的話,那么任何一個客戶端連接連入之后它都需要一個套接字文件來維持這個連接。因為Linux要靠此特性來達到一切皆文件的哲學思想或者說這樣一種目的。因此,站在這個角度來講,如果我們Nginx下能夠支持1萬個並發連接,那么它至少需要維持1萬個套接字文件,還不包括自己打開自己的配置文件。對於要想能夠支持更大的並發連接數,我們對應的每一個worker進程是不是應該設置它所能夠單進程打開的文件數量提升呢?因為worker進程是以Nginx用戶的身份來運行的,因此我們為了能夠worker進程接受更多的並發連接,運行worker進程的用戶得能夠同時打開更多的文件才可以的。默認情況下的1024對於一個高並發的worker進程來講顯然是太小太小了,所以必要時我們可以通過調整此值對應於設置一個worker進程所能夠打開的最大文件數。
worker_rlimit_core size;
用來指明所有的worker加起來所能夠使用的總體的核心文件的最大大小。指的就是這個進程所能夠使用的核心數量的或者說核心文件的最大文件體積大小。此指令一般很少需要我們手動調整。
- 性能優化相關的配置:
worker_processes #;
指定所能夠打開的worker進程的個數;通常應該為物理CPU核心數-1或-2,即通常應該略少於CPU物理核心數;要留出一個核為系統自己所使用。但是現在Nginx的版本支持它的值為auto,Tengine可以設置為auto表示自動設定,自動打開幾個,自動判斷。
worker_cpu_affinity cpumask …;
設定把worker進程綁定在哪些CPU上的。此處指明CPU用的是cpumask,cpumask為CPU掩碼,CPU掩碼指的是什么呢?假如主機上有四顆CPU,它可能用8位二進制來表示,那這四顆CPU怎么標識呢?在其對應上為1就表示那一顆CPU被選中了,如一號CPU為0000 0001、二號CPU為0000 0010、三號CPU為0000 0100,四號為0000 1000。所以這個worker_cpu_affinity后面所跟的如果只綁定在前三顆上面,只寫前三個就可。
例如:worker_cpu_affinity 00000001 00000010 00000100;
但例如:0011表示第一和第二同時兩顆。
對於Nginx來講,我們說過,它的工作特性是一個線程或者一個worker進程響應n個用戶請求的。那么即便我們啟動少於CPU的物理核心數的進程,但是它依然面臨一個問題,如我們當前這個主機上所同時運行的進程數量可能會比較多,百十個進程應該是很常見的,所以即便有4個物理核心,在某一時刻,在當前CPU上所運行的也未必是Nginx的worker進程。那結果就是對於非常繁忙的worker進程來講,它有可能被頻繁的調度來調度去。那很有可能我們啟動了假設叫三個進程,這三個進程到底誰運行在哪個CPU上是沒法確定的。我們Linux的核心會動態的根據當前CPU的空閑數以及需要運行的進程數調度,因此可能是不斷的在進行變化。這樣一來帶給我們的最大壞處在於由於在CPU上需要做進程切換(context switch),進程切換會消耗時間會浪費CPU,所以進程切換本身會帶來額外的CPU消耗,會產生CPU的不必要的消耗,因此為了盡可能的壓榨CPU,發揮CPU的最后一點一滴的性能,這就是我們要設置進程數要少於CPU核心數的原因。
我們可以這么做,明確的指明把每一個進程綁定在指定CPU上,比如一號進程明確說明綁定在一號上面,它絕不在其它CPU核心上運行,二號進程綁定在二號CPU上,三號進程綁定在三號CPU上。所以這樣一來,這三個進程一定不會被調度到其它核心上去了,但是其實這樣是不可能避免進程間的切換的,因為我們雖然把它綁定在這顆CPU上並不意味着這個CPU不能運行其它進程。但是至少保證了這個對於Nginx這一個進程本身不用調度到其它CPU上去了,那帶來的好處是這個進程自己的緩存不會失效,說白了就是如果說這個Nginx在運行時,我們這個Nginx進程加載了很多數據到CPU的二級緩存、一級緩存、一級指令緩存包括一級數據緩存,如果我們不幸的把它調度到另外一顆CPU上去,那在第一顆CPU的緩存的內容就會失效了,到另外一顆CPU緩存還需要重新加載的,所以我們一旦把進程綁定的話,我們這個進程自己曾經加載到CPU上的緩存即便這個進程被調度出去,等一會它在回來運行時,那些緩存卻依然有效,所以保證了緩存的有效性。但是它卻沒辦法避免進程切換。
優點:能夠提升CPU緩存的命中率;
但是不能避免context switch,我們真正要想極端去優化Nginx所在的服務器的話,還有一種方法;這種方法可能需要一些別的技能來支撐了。如:開機的時候設定主機上四顆CPU有三顆都不被內核使用,這叫CPU隔離,所以一開機我們就事先說明,內核只能使用這一顆CPU。那這樣子一來,內核想調度任何進程就只能在第四顆CPU上調度。那開機完成以后,我們還要隔離中斷,我們系統可能有很多中斷,中斷可能由硬件產生也可能是軟中斷,此處僅指硬件中斷,硬件中斷包括網卡上有報文到來或者我們的硬盤上有IO完成等等。這些也都從CPU上隔離,所以它不處理中斷,也不接受任何進程調度。當啟動完以后,我們明確說明第一個進程綁定在第一顆,第二個進程綁定在第二顆,第三個進程綁定在第三顆。那這三個進程不受內核調度,所以再也不會有其它進程到這顆CPU上運行了,那這就意味着專CPU專用,第一顆CPU只為運行Nginx,Nginx再也不會被切換出去了。所以這個Nginx但凡需要運行,它就自己牢牢霸占住第一顆CPU,充分發揮CPU每一時刻的運算能力來支撐Web服務的訪問。
那這樣一來,三個worker線程啟動起來以后,假如說每一個worker線程可以支持1萬個並發,那三萬個就很輕松拿下了。但是這需要做兩個前提,第一,我們在啟動OS時需要隔離出來這三顆CPU,第二,我們需要把中斷處理從這三顆CPU上剝離出去才可以。
timer_resolution interval;
計時器解析度:降低此值,可減少gettimeofday()系統調用的次數;
這種無謂的系統調用次數或者是我們用不到那么高的時候,降低這個值減少這個次數會在一定意義上提升Nginx性能的或者提升我們整個系統性能的。對於一個非常繁忙的服務器這點是不容忽略的。
可用來減少降低worker線程中的計時器的解析度,降低解析度以后帶來的結果是,說白了就是減少發起gettimeofday()這么一個系統調用,任何一個進程運行過程當中,如果系統調用很多,任何一次系統調用的發生必然會產生軟中斷,產生軟中斷的直接結果就是模式轉換,每一次的模式轉換必然會消耗時間。因為我們說過很多次,一個生產力非常強的進程應該把大量時間用在用戶空間,那因此timer_resolution這個時間解析度為什么要用到這個東西呢?
我們簡單舉個例子:比如任何一個請求到達Nginx的時候Nginx就得響應它,響應完以后就得記錄日志了,但是到日志中會記錄時間。那因此一旦有用戶請求到達,我們這邊處理響應而要記錄日志的話那么它必須要獲取系統當前時間並記錄在日志文件中。怎么獲取系統時間呢?通過gettimeofdate來獲取時間而后把這個結果再保存到日志文件中。而如果我們的時間解析度過大的話,就可能帶來的結果是他一秒鍾就解析一千次,一秒鍾就發起一千次系統調用,如果我把這個降低一點,比如100ms解析一次,那就意味着一秒鍾只解析10次了。如果指定1s一次那就意味着1s鍾只解析1次,但是我們知道時間解析度越低它的精准度就越小,時間解析度越高精度就越高,正如我們的顯示器一樣,分辨率越高,顯示的就越逼真越清晰但是它所需要消耗的資源一定也是越大的。
那因此為了提高性能,這個時間解析度應該是降低的。只要我們對於它的時間解析度要求不是特別高的話。
worker_priority number;
指明worker進程的優先級(nice值)。
但是這個優先級是使用nice值來指明的,說白了其實這里是指明nice值
nice值的范圍是-20~19,-20對應的優先級是100,19對應的優先級是139。數字越小優先級越高。默認情況下每一個進程它的nice值都是0,因此其優先級為120,數字越小優先級越高,優先級越高它越被優先調度到CPU上被運行。
例如:worker_priority -20;指定其100的優先級。那么如此高的優先級就說明我們的Nginx但凡有可能,只要沒有比它優先級更高的,那么他將會被排在前面優先被調度到CPU上運行。這對於一個Web服務器來講尤其是反代模式下的Web服務器或者是我們支持更高並發的Web服務器來講這算是一個比較有效的設定。再次提醒,nice值越小,其對應的優先級越高。但是只有管理員才有權限調低nice值,好在我們啟動Nginx的時候是以root用戶啟動的。只不過運行worker時,它們才會由nginx普通用戶來運行它,在此之前它可以完全以root用戶的身份來設定其nice值。
以上是與系統優化相關的指令,這四項尤其是前兩項,幾乎任何時候我們新配置一個Nginx都要自己手動指定,務必要理解他們指的是什么。
- 事件相關的配置:
即放在event當中的一般而言用於使用的相關配置。
accept_mutex {on|off};
master調度用戶請求至各worker進程時用的負載均衡鎖;on表示能讓多個worker輪流的、序列化的去響應新情求;
mutex稱為互斥鎖,所謂互斥鎖就是獨占的,一般而言,任何一個進程持有鎖以后其它進程都是被排除在外的,叫排它鎖。當然這樣解釋其實並不精確,只是為了能夠便於理解的。那么這個accept_mutex有什么用呢?
內部調用用戶請求至各worker時的負載均衡鎖,這是一個。說白了,作為Nginx而言,它是由一個主控進程master生成多個worker進程響應用戶請求的,問題是當一個請求到達時我們應該使用哪一個worker來響應它呢?因為worker和請求之間並不是一一對應的,所以當一個新的用戶請求到達時,新的用戶請求主控進程必須從這多個worker進程中來響應的,挑選誰呢?而accept_mutex其實就是實現這個功能的。
如果說我們打開此功能,即啟用了accept_mutex,表示讓多個worker輪流的序列化的響應新請求。就是有先有后的、一個挨一個的來響應新的用戶請求,否則的話,我們的master有可能隨機的。
accept_mutex_delay time;
如果說accept_mutex啟用了,如果某一個worker正在嘗試接受用戶請求的時候,那其它用戶請求我們要延遲多長時間等待。所以要不然怎么叫互斥鎖呢?一個請求來了大家不能搶,輪到誰了就讓誰來響應。但是這個worker有可能會忙不過來,那就需要指定延遲或等多長時間。這么長時間以后,則有可能會選擇為下一個。這個不是特別關鍵;
lock_file file;
accept_mutex用到的鎖文件路徑(互斥鎖鎖文件路徑);
既然我們指明接受用戶請求的互斥鎖,那么這個鎖的鎖文件應該是誰,就是lock_file。我們在編譯時其實指明過這個鎖文件了。
use [epoll|rtsig(實時信號)|select|poll];
用於指明使用的事件模型或者是復用模型機制;不建議手動指定,建議讓Nginx自行選擇;
use method;注意此處的method可不是http協議的請求方法,而是指明連接處理方法,什么叫連接處理方法?對於一個並發服務器來講,有多個用戶請求到達時,我們怎么去處理用戶請求呢?有多種機制,像基於事件驅動、基於復用器、基於select()來響應、基於poll()來響應、基於epoll()來響應或者叫基於event driven來響應等等。所以這里的use定義使用的事件模型的。
Nginx會自行實現最優化選擇,比如在Linux主機上能使用epoll它就不會使用select,我們說過epoll才是真正支持事件驅動的機制,你要使用select就跟我們的httpd prefork沒什么區別了。它的並發連接數也勢必會大大的降低的。所以說對於Nginx而言,雖然我們說它是支持使用event driven機制甚至是支持AIO的,但是事實上作為用戶來講,可以指明我們到底使用哪一種類型,就使用這個指令進行指定。
worker_connections #;
設定單個worker進程所能夠處理的最大並發連接數量;
worker的連接數,即一個worker進程所能夠接受的最大並發連接數。
例:假設worker_connections 10240;那請問當前Nginx最多能接受的最大並發連接為多少個?不是10240,這取決於worker_proccesses有多少個,如果其定義了為3個,這里定義為10240,那就是3*10240。當然如果前面定義為8個,此處定義為10240也沒用,因為不可能能達到8萬多個,因為我們的套接字有限,但是至少我們此處的這個數字不應該成為限制。
因此它所能夠接受的最大並發連接數等於worker_connections*work_processes。但是它有可能會小於這個值,因為這個值如果設定的大於65535個的話,一般來講他就不會真正有效了。為了避免單個進程本身不會因為的數量小於這個值的話,可以直接定義worker_connections為51200,很多人的配置中可能是這么定義的。所以說,這個參數也是經常必須要調的參數之一。
- 用於調試、定位問題:
注意:要想能夠打開調試功能,我們在編譯時必須要使用—with-debug這個選項才可以,否則調試功能是沒辦法啟用的。
daemon {on|off};
是否以守護進程方式運行Nginx;調試時應該設定為off;
說白了就是如果為on就把它運行為守護進程,如果off就不運行為守護進程。不運行為守護進程就意味着它運行在前台了。運行在前台它可以把各種日志或者錯誤信息輸出到控制台上來,我們可以在控制台上看到。這叫做daemon模式,所以在調試時將它定義為off,其它情況下應該設定為on。
master_process {on|off};
是否以master模型來運行Nginx;調試時可以設置為off;
正常情況下應該以這種master模型運行Nginx,調試時可以設定為off;這就表示,你如果出於調試的目的只啟動一個worker進程直接接受用戶請求就不會說一個主進程一個子進程,因為主進程子進程兩種方式不容易調試問題所在。
error_log file | stderr | syslog:server=address[,parameter=value](記錄到遠程服務器) | memory:size(記錄到內存中,內存大小) [debug | info | notice | warn | error | crit | alert | emerg];
簡單來講,格式為:
error_log 位置 級別;
若要使用debug級別,需要在編譯Nginx時使用了—with-debug選項;
這是用來而配置日志功能的,所有的錯誤日志相關信息都會指明保存在某文件中,用於幫我們去排錯的。簡單來講,它的格式其實只有三段組成,第二段主要用於指明記錄到哪,第三部分主要用於指明日志級別的。默認情況下,我們通常都記錄在本機指定位置下,而級別有可能為對應的warn或者notice。
再次說明,只有使用—with-debug打開了調試功能時,我們也才可以使用debug這個級別。否則即便指定了這個指令也無效。
以上是Nginx主配置段中常用到的一些參數,我們做了一些簡要的說明。
總結:常需要進行調整的參數
再次說明,我們將來在配置時,最常用需要修改的四個參數為:worker_proccesses、worker_connections、worker_cpu_affinity以及worker_priority。都跟worker相關。
示例:
若期望修改的配置生效:
這樣Nginx就能夠讀取新配置了。
只要不改Nginx端口,只需要使用nginx –s reload即可生效。
新改動配置生效的方式:
nginx –s reload #這表示向nginx的主控進程傳一個參數
除了reload接受的其它參數還有:stop、quit、reopen
三、Nginx作為WEB服務器時使用的配置
Nginx所有跟WEB相關的配置都放在http這個上下文中,所以我們稱之為http的上下文或http的配置段。
這個配置段是由httpd的核心模塊引入的,我們前面說過,Nginx的配置文件或者我們整個Nginx它是由多個模塊組成的,這個模塊其中有核心模塊,像我們剛才講的都是由核心模塊提供的。那除了Nginx自己的核心模塊之外,他還有http的標准模塊、有可選的http模塊還有郵件相關的模塊以及第三方模塊。在這些眾多模塊當中,其中有一個叫做標准http的模塊,而標准http的模塊中有一項叫做http core,說白了就是為了提供http協議相關的功能而提供的一個核心模塊。它用於實現控制端口、location、錯誤頁面、別名以及其它一些最基礎的相關配置,我們稱為HTTP core。
http {}:由ngx_http_core_module模塊所引入;
所以我們只要用這個模塊就會用到http的相關配置的部分。它是用來做什么的?說白了,http core是用來配置一個靜態的WEB服務器,它能夠作為像httpd一樣來工作的靜態WEB服務器。我們如果想讓它工作在反向代理模式下,需要使用額外的反向代理模塊來實現。
這個配置段當中的配置框架或配置主體部分:
大體上遵循這樣的格式,首先:
http { upstream { #用到負載均衡或反向代理時會引入的上下文 … } server { location URL {#有點類似於DocumentRoot,但是location跟DocumentRoot還不完全一樣,因為對於我們的VirtualHost來講DocumentRoot只能有一個,
#但是對於一個虛擬主機server來講,location可以有多個。所以它有點類似於我們對應與httpd中的location容器,用來定義URL和本地的對應關系的。 root “/path/to/somedir”; … }#類似於httpd的<Location>,用於定義URL與本地文件系統的映射關系;所以我們說在一個server中可以有n個location,
#因為在一個server中它對應的那個站點的URL的起始事件可以多個,比如www.magedu.com下的images、bbs等等,每一個路徑都可以單獨分別指定的把網頁放在不同位置。
#就這一點而言,它事實上比httpd要靈活。同一個路徑或同一組URL下可以映射多組不同的路徑。而在location中,它還可以對應一個URL,這個URL對應在什么地方呢?
#那一般而言,它由於要對本地文件系統建立映射關系,所以這里邊通常會有一個root用來本地文件系統路徑。 location URL { if … { … } }#同時,在location中還可以引入一個新的上下文叫if語句;如果符合某個條件,那么應該…。它還可以有條件判斷的功能; } #每一個server有點類似於httpd中的<VirtualHost>;所以它是用來定義虛擬主機的 server{ … } }
注意:與http相關的配置指令必須僅能夠放置於http、server、location、upstream、if上下文中。但有些指令僅應用於這5種上下文的某些中而非全部,即不同指令它的應用位置、能夠生效的位置各不相同。
這些配置框架中經常用到的配置指令:
- server{}
定義一個虛擬主機;
演示效果:
這是基於端口的虛擬主機,基於IP的只需不同的listen IP:PORT,基於名稱的時序server_name不同。
- listen
指定監聽的地址和端口;
listen address[:port];
listen port;
(可以只指端口不指地址表示默認監聽本機上的所有地址,也可以只指地址不指端口表示默認監聽在80端口)
對於Nginx來講,其listen有三種格式:
此前說過,Linux主機上表示套接字的地址家族有三種方式,一IPv4二IPv6三本機上通信可以基於Unix Socket文件。所以說我們對應的Nginx的虛擬主機也可以listen一個Unix的path路徑,表示本地的一個socket類型的文件,一旦監聽在socket類型的文件上,這意味着客戶端只能是本機了。
那這么復雜的指令到底是所謂哪般呢?為何要這么復雜呢?它所指明的這些參數應該非常容易理解,像:
ssl:表示如果我們要啟用ssl功能,應該指定ssl;
spdy:這是Google改進的http協議的版本,它比1.1更加優越,但是現在http2.0版本已經出現了,依然取代spdy這種格式了。若有興趣,可自行了解。
rcvbuf:接收的緩沖的大小;
sndbuf:發送的緩沖大小;
accept_filter:接收時使用的過濾器;
所以我們在一個listen指令上可以定義非常復雜的選項,當然對於此處而言我們完全沒必要搞得這么麻煩,只需要知道我們有一個listen指令可以指定監聽的地址和端口即可。
它的更為復雜的功能其實我們只有在特殊情況下單獨設定其接受緩沖和發送緩沖大小時偶爾才會用到,所以不再說其更為復雜的功能。
- server_name NAME […];
指定主機名,后可跟多個主機名;名稱還可以使用正則表達式(使用正則表達式時需要以~開頭作為引導)或通配符;
server_name只能用在server中,別的地方不能使用,所以每一個指令的context表示他可用於的上下文是有要求的。
當我們有多個server時,當用戶使用其中的某一個server時,我們應該匹配到哪一個server上?如:
server{ server_name www.test.com; } server{ server_name *.test.com }
當用戶匹配到www.test.com應該匹配到哪一個上面?
所以此處有做匹配時的匹配法則:
(1)先做精確匹配檢查;
也就是說若你訪問到的主機名剛好與某一個server主機名一模一樣,而且他沒有所謂的通配的概念。那么這個就完全匹配到了,所有優先級最高的為精確匹配檢查。
(2)左側通配符匹配檢查;
如寫成*.magedu.com,當用戶去訪問mail.magedu.com,第一個不匹配,於是去檢查第二個。而第二個這個通配符正好在左側,而有的是這樣寫的:
server { server_name www.test.com; } server { server_name *.test.com; } server { server_name mail.*; }
當用戶訪問mail.magedu.com的時候,第二個與第三個都能匹配,如果左側通配符能匹配於是就立即生效,否則再去檢查右側通配符。
(3)右側通配符匹配檢查,如mail.*;
(4)正則表達式匹配檢查,如:~^*\.magedu.com\.com$;
(5)default_server;
若默認服務器未定義,那就自上而下找第一個server來匹配。
所以說,當用戶請求到達時,而我們當前主機上定義了n個虛擬主機,那我們應該應用在哪個虛擬主機之上,事實上是由server_name后面這么一個固定的檢查法則檢查完以后進行確定的。
- root path;
設置資源路徑映射的;用於指明請求的URL所對應的資源所在的文件系統上的起始路徑;
放置的容器越大,范圍越大,優先級越低。
- location
兩種用法:
location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }
根據請求的URI設定特定請求的配置的。
功能:允許根據用戶請求的URI來匹配定義的各location;匹配到時,此請求將被相應的location配置塊中的配置所處理,例如做訪問控制等功能;
例:
當用戶在瀏覽器中輸入http://www.test.com/bbs/時,它到底應用在哪個location上呢?第一個與第三個能夠匹配到,那到底以哪個為准呢?所以說它也有相應的優先級了,比如:
=:精確匹配檢查;
即錯一個字符都不行;
因此如果訪問的是http://www.test.com/,而且location = /{...},那第一個就能匹配,但如果寫的是http://www.test.com/bbs/,並且location寫的是=,那它就不能被第一個匹配了。因為第一個必須是根,后面不能帶任何東西,這叫做精確匹配。如果不帶=表示,則起始路徑的都可以被匹配。
~:正則表達式模式匹配檢查,區分字符大小寫;
~*:正則表達式模式匹配檢查,不區分字符大小寫;
^~:URI的前半部分匹配,不支持正則表達式;
匹配的優先級:精確匹配(=)、^~、~、~*、不帶任何符號的location;
驗證:
因為此時匹配到的第三個到/vhosts/text下去找images,然后再images下找a.txt。
對於Nginx而言,location是非常關鍵的。
- alias path;
用於location配置段,實現定義路徑別名;
這與httpd中的alias並沒有兩樣。用來實現路徑映射;
注意:root表示指明路徑為對應的location “/” URL;也就意味着你怎么指root,它都相對於”/”這個起始路徑而言的。像剛才我們訪問images下面的a.txt,這個時候images本身也是相對於這個對應的/vhosts/下面補上這個images而言,說白了你無論路徑怎么映射,它是起始於/images而言的。
alias表示路徑映射,即location指令后定義的URL是相對於alias所指明的路徑而言;
如:
root:
定義:
location /images/ {
root "/vhosts/web1";
}
例如:
訪問http://www.test.com/images/a.jpg表示訪問的是服務器路徑:/vhosts/web1/images/a.jpg
alias:
定義:
location /images/ {
alias "/www/pictures";
}
例如:
訪問http://www.magedu.com/images/a.jpg表示訪問的是服務器目錄下的:/www/pictures/a.jpg
驗證:
對於alias而言這個location URL路徑右側的根相對於這個整個alias后面的根,所以在images下的所有文件是直接在這個/vhosts/web1目錄直接去找的。而root是images左側這個根相對於/vhosts/web1而言。
- index file
這是一個新的叫做index的模塊所提供的;
默認主頁面:
index index.php index.html;
表示有兩個頁面自左而右可以作為主頁面,先搜索index.php找不着的話再去找index.html;
- error_page code […] [=code] URI | @name(錯誤碼或響應碼或狀態碼可以有多個)
根據http響應狀態碼指明特用的錯誤頁面;
例如:
error_page 404 /404_customed.html
說明:404_customed.html放在什么路徑下都可以,看error_page定義在什么位置,對應的就相對於那個對應你所所在的location中的root而言的。
[=code]:可省略,如果要定義的話,表示以指定的響應碼進行響應,而不是默認的原來的響應碼;(比如找不到頁面可能用404響應,但我把它定義成302就可以了);默認表示以新資源(即指定的網頁)的響應碼為其響應碼。
例如:
這是Nginx自帶的404錯誤頁面;
自定義:
使用-t可以測試語法錯誤;
我們還可以自行定義它的響應碼,如:
- 基於IP的訪問控制機制
兩個相關指令:
-
- allow IP/Network;
- deny IP/Network;
基於IP的訪問可以用在http、server、location中,看你的訪問控制有多大的范圍。
舉例:拒絕192.168.241.1訪問演示:
只允許192.168.241.0網段訪問:
- 基於用戶的訪問控制
支持兩種認證方式:basic、digest
要想做基於用戶的訪問控制,只需要在需要做訪問控制的配置段中使用如下指令:
auth_basic “”; #相當於httpd的AuthName; auth_basic_user_file “PATH/TO/PASSWORD_FILE”; #跟用戶相關的賬號密碼文件的位置;這個文件盡可能隱藏起來,因為不是所有用戶都會使用ls –a 來查看這個文件的,
#所以隱藏一下總然是由好處的。
賬號密碼文件建議使用htpasswd創建;htpasswd這個命令是由httpd提供的,因此需要安裝httpd。
- https服務:
生成私鑰,生成證書簽署請求,並獲得證書;
演示示例:
顯示不可靠,因為瀏覽器沒有導入CA的證書,所以它沒辦法驗證是否可靠。
- stub_status {on|off};
狀態頁的啟用或關閉;
此指令僅能用於location上下文中;
在此結果示例中:
Active connections:活動連接數;指的是當前所有處於打開狀態的連接數。如果向客戶端代理的話也包括代理的。 server accepts handled requests #表示接收下來並已經處理的請求; 70 70 47 #第一個數字表示已經接受過的連接數,第二個為已經處理過的連接數,第三個為已經處理過的請求數。由於我們可能已經啟用了連接處理功能,
#所以請求數可能會大於連接數。在“保持連接”模式下,請求數量可能會多於連接數量。
Reading:正處於接收請求狀態的連接數;
Writing:請求已經接收完成,正處於處理請求或發送響應的過程中的連接數;
Waiting:保持連接模式,且處於活動狀態的連接數;(要么處於正在接收請求要么正在發送請求的狀態中的連接數)
- rewrite regex replacement flag;
指的是URL rewrite,說白了是實現URL重寫的。你本來請求的是某一個特定URL,服務器處理之后將URL悄悄的換成別的URL。
例如:
rewrite ^/images/(.*\.jpg)$ /imgs/$1 break;
它所帶來的效果是,比如說用戶如果請求的是http://www.magedu.com/images/a/b/c/1.jpg,那它會把這個路徑自動換成http://www.magedu.com/imgs/a/b/c/1.jpg
URL重寫在什么時候用到呢?比如,我們網站從a域名改到b域名了,但是兩個域名都是我們的,我們期望用戶以后訪問時看到的都是b域名,所以所有對於a域名的訪問都改成b域名下的相關地址也是可以的。再不然,我們本來的網站某個頁面路徑在某個URL下現在換成新的URL像我們這里的images換成imgs也可以基於URL重寫實現。
當然在完成某些SEO時也有用,比如說我們把這個動態的路徑改成靜態的方式,那么這會使得我們緩存服務器能緩存下來而且SEO時還能提高我們的權重。
不過此處的flag卻是大有講究的;
flag:標識位
-
- last:一旦被此rewrite規則重寫完成后,就不再被后面的其它rewrite規則進行處理;而是由User Agent重新對重寫后的URL再次發起請求並從頭開始執行類似的過程。
如:匹配規則有n條,用戶請求來之后,檢查第一條不匹配、第二條不匹配、…、第四條匹配了,后面的就不檢查了。此時因為一重寫已經成為了一個新的URL,這個新的URL會再次被重新發給Nginx服務器,Nginx 服務器一樣會從第一條檢查、第二條、…,如果檢查第三條又被重寫了,因此又一次改成新的URL再次發起請求從頭到尾來檢查。
完成當前這個重寫規則以后,重啟處理機制。
例如:我們訪問http://www.magedu.com/images/a/b/c/1.jpg通過URL重寫會轉換成http://www.magedu.com/imgs/a/b/c/1.jpg,對於這個新的請求來講,瀏覽器會重新用新的URL對Web服務器發起新的請求,Web服務器即Nginx收到這個新情求后一樣會像此前處理任何一個請求一樣處理這個請求,即此時還會再次檢查這個規則,如果這個規則又能完全匹配到它的話,還會再次重寫,所以這叫重啟。所以第一個請求我們通過規則檢查匹配到了,所以改成新的URL了,所以瀏覽器此時會重新對這個鏈接發新情求,這個新請求會經過規則的再次檢查,如果檢查以后沒有被匹配,因此就不會被重寫,於是請求就往后繼續處理了。
-
- break:一旦此rewrite規則重寫完成之后,由User Agent對新的URL重新發起請求,且不再會被當前location內的任何rewrite規則所檢查;新請求的URL就不會再被任何重寫規則所檢查,直接進行后續處理。
什么時候用到break呢?就是在很不幸的情況下,兩條規則可能造成循環。例如:
... rewrite ^/images/(.*\.jpg)$ /imgs/$1 last; rewrite ^/images/(.*\jpg)$ /images/$1 last; ... 如:訪問http://www.test.com/images/a/b/c/1.jpg <--> http://www.test.com/imgs/a/b/c/1.jpg
-
- redirect以302響應碼(臨時重定向)返回新的URL;
什么時候才會用302響應呢?如果說我們替換成一個目標位置是以http協議開頭時是一個新的絕對路徑時,可以以redirect實現。
-
- permanent:以301響應碼(永久重定向)返回新的URL;
舉例:
- if
語法:
if (condition){…}
應用環境:server、location;
condition:
(1) 變量名;
變量值為空串,或者以“0”開始,則為false;其它的均為true;
如果變量為空串,即變量沒有值的話表示為假否則為真。因此任何空串或者任何為0的值甚至於任何以0起始的字符串其都為假否則則為真。
(2) 以變量為操作數構成的比較表達式
可使用=、!=類似的比較操作符進行測試。
比如,看看某個變量的值是否是我們所請求的某個值。
(3) 可實現正則表達式的模式匹配
~:區分大小寫的模式匹配檢查;
~*:不區分大小寫的模式匹配檢查;
!~和!~*:對上述兩種測試取反;
需要注意的是,並不是在配置文件中用到模式正則表達式越高級越好,不用正則表達式是最好的,因為正則表達式引擎在匹配時必然會消耗更多的CPU使用周期,會降低性能的。
(4) 測試路徑為文件的可能性
-f:表示是否存在;
!-f:表示是否不存在;
(5) 測試指定路徑為目錄的可能性
-d
!-d
(6) 測試文件的存在性(不管是目錄還是文件)
-e
!-e
(7) 檢查文件是否有執行權限
-x
!-x
例如:
if($http_user_agent ~* MSIE){ #若瀏覽器類型為MSIE,即為微軟瀏覽器 rewrite ^(.*)$ /msie/$1 break; } #http_user_agent為內建變量,表示客戶端瀏覽器的類型;
有些時候的URL重寫是必須的,像根據客戶端瀏覽器的不同返回不同的站點,定向至不同的位置上去。這樣可以讓用戶有更好的用戶體驗。可以想象用戶使用手機打開電腦版的頁面的話那該是多么痛苦。
- 防盜鏈
假如站點的圖片只允許站內鏈接或者不是我們所允許的域名通常都不允許鏈接。
locating ~* \.(jpg|gif|jpeg|png)$ { valid_referer none blocked www.test.com; #validreferer是Nginx自帶的一個內置的指令,它用於定義哪種引用是合法的。valid表示合法的、允許的,referer表示引用者,因此valid_referer表示合法的引用者是誰。none blocked表示不做阻止的。 if($invalid_referer){ #$invalid_referer是我們的refereer模塊所引入的一個變量,它表示但凡不能夠被上面定義為合法引用的,統統都會歸到不合法引用上面,所以如果說這個變量的值不為空,就說明這一次引用可能是不合法的。因為為空就因為上面的已經被匹配了。 rewrite ^/ http://www.test.com/403.html; } }
- 定制訪問日志格式
使用log_format指令進行定義,類似於在httpd中使用的log_format指令;
如果想定義成兼容給我們對應的日志格式一模一樣的話,此處要使用log_format,並查找服務器端所提供的或者服務器上模塊所提供的內置的變量來類似使用兼容格式,叫combined來進行定義。log_format后接的為格式名。一旦我們要想自己定義訪問日志的話,千萬要記得要使用access_log,如:
五、網絡連接相關的配置
我們將來把Nginx作為一個反代服務器時有可能會用到。
例:前端使用Nginx作為反代服務器,后端使用Tomcat作為Web服務器,通過Nginx訪問Tomcat時總是會出現503超時,而直接訪問Tomcat時是正常的,但是通過前端Nginx訪問偶爾總是會出現超時,代理服務器上去取后端內容超時。出現這種情況推測很有可能是Nginx反代時它會自己構建一個請求報文到后端Tomcat,但是構建這個請求等待Tomcat響應的時間默認設置的過短,而Tomcat自己的處理可能是需要一定的時長的。一旦這個時間過短的話,對方還沒響應過來就超時了,事實上響應是正常的。
這一些都跟網絡連接相關,所以這個時候我們就不得不調節這些跟網絡連接相關的超時時長。
- keepalive_timeout #;
保持連接或長連接的超時時長,默認為75s;(對於負載均衡器來講,75有點大)
- keepalive_requests #;
在一次長連接上所能夠允許請求的最大資源數;
- keepalive_disable [msie6|safari|none];
為指定類型的User Agent禁用長連接;
禁用長連接,但是這種禁用並不是禁用整個長連接的功能,而是只為特定類型的瀏覽器禁用長連接,因為有些瀏覽器在長連接的支持上是功能有限的。
- tcp_nodelay on|off;
是否對長連接使用TCP_NODELAY選項;
一般來講,如果想提高用戶體驗的話,讓用戶一請求立即就能得到響應,我們還是應該啟用tcp_nodelay,不讓它延遲,每個包都單獨發送,無論它有多小。
tcp是一種開銷比較大的連接類型,因為任何一次數據發送之前都要先三次握手而后要四次斷開。如果很不幸的是,某一個連接恰好它所發送的數據量非常小,訪問這個頁面下一共也只有幾個字節或者十幾個字節而已,如果我們請求網站上的資源很多都是這很小的資源的話,會發現每一個包單獨封裝而且沒有使用長連接結果是大量的資源開銷被浪費掉了,那為了避免這么一種浪費,TCP在它的擁塞避免算法中有一種解決方案是它把多次請求的小資源合並成一次大的響應過去,比如請求了很多東西每一個都很小,把多個小的請求合並成一個響應報文響應過去了。這樣子可以節約帶寬可以節約請求次數。但是這樣一來會有問題,想象一下對於Web站點來講,用戶請求一個資源半天還沒響應,為什么呢?他等待跟其他包合並呢?這是無法接受的,所以在有些本來就具有的確擁有很多小資源的Web站點上,我們不應該啟用tcp_delay的功能,所以我們把tcp延遲功能關閉,tcp_nodelay就表示tcp是不是不延遲,on就可以。
- client_header_timeout #;
讀取http報文請求首部的超時時長;
如果我么自己的服務器很繁忙自己帶寬不夠用,讀別人讀不到,因為別人擠不進來,那這個時候應該把此時長調長一些以便能讀進來別人的請求,不過即便時間延長用戶體驗也是很差了。
一個客戶端發請求過來了,所有的數據流依然都是要編碼成數據流式報文進行請求的。那我們讀了第一個首部、讀第二個首部、…但是客戶端的帶寬太小了,半天讀不到一個,那到底應該等多長時間呢?
- client_body_timeout #;
讀取http請求報文的body部分的超時時長。
- send_timeout #;
發送響應報文的超時時長;
六、fastcgi的相關配置
Nginx與php結合時他不能像httpd與php結合一樣把php直接做成Nginx的模塊,這是不支持的,所以如果你想構建LNMP,方法只能有一個,php要啟用為fpm模型。
在Nginx上要想配置用戶請求php頁面時能夠發給fpm的php來提供服務的話,方法非常簡單,配置文件中有示例:
fastcgi_params文件中定義了如何把用戶請求時的變量值映射提供給后端的fastcgi服務器的。