看下面這張圖,這張圖是 Nginx 處理 HTTP 請求的示意圖,雖然簡單,但是卻很好的說明了整個過程。
以上這七個步驟從整體上介紹了一下處理流程,下面還會再說一下實際的處理過程。
下面介紹一下詳細的 11 個階段,每個階段都可能對應著一個甚至多個 HTTP 模塊,通過這樣一個模塊對比,我們也能夠很好的理解這些模塊具體是怎么樣發揮作用的。
接下來是確認用戶訪問權限的三個模塊:
最后的三個階段處理響應和日志:
以上的這些階段都是嚴格按照順序進行處理的,當然,每個階段中各個 HTTP 模塊的處理順序也很重要,如果某個模塊不把請求向下傳遞,后面的模塊是接收不到請求的。而且每個階段中的模塊也不一定所有都要執行一遍,下面就接著講一下各個階段模塊之間的請求順序。
如下圖所示,每一個模塊處理之間是有序的,那么這個順序怎么才能得到呢?其實非常簡單,在源碼 ngx_module.c 中,有一個數組 ngx_module_name,其中包含了在編譯 Nginx 的時候的 with 指令所包含的所有模塊,它們之間的順序非常關鍵,在數組中順序是相反的。
char *ngx_module_names[]={
… …
"ngx_http_static_module",
"ngx_http_autoindex_module",
"ngx_http_index_module",
"ngx_http_random_index_module",
"ngx_http_mirror_module",
"ngx_http_try_files_module",
"ngx_http_auth_request_module",
"ngx_http_auth_basic_module",
"ngx_http_access_module",
"ngx_http_limit_conn_module",
"ngx_http_limit_req_module",
"ngx_http_realip_module",
"ngx_http_referer_module",
"ngx_http_rewrite_module",
"ngx_http_concat_module",
… …
}復制代碼
灰色部分的模塊是 Nginx 的框架部分去執行處理的,第三方模塊沒有機會在這里得到處理。
在依次向下執行的過程中,也可能不按照這樣的順序。例如,在 access 階段中,有一個指令叫 satisfy,它可以指示當有一個滿足的時候就直接跳到下一個階段進行處理,例如當 access 滿足了,就直接跳到 try_files 模塊進行處理,而不會再執行 auth_basic、auth_request 模塊。
在 content 階段中,當 index 模塊執行了,就不會再執行 auto_index 模塊,而是直接跳到 log 模塊。
整個 11 個階段所涉及到的模塊和先后順序如下圖所示:
下面開始詳細講解一下各個階段。先來看下第一個階段 postread 階段,顧名思義,postread 階段是在正式處理請求之前起作用的。
postread 階段,是 11 個階段的第 1 個階段,這個階段剛剛獲取到了請求的頭部,還沒有進行任何處理,我們可以拿到一些原始的信息。例如,拿到用戶的真實 IP 地址
我們知道,TCP 連接是由一個四元組構成的,在四元組中,包含了源 IP 地址。而在真實的互聯網中,存在非常多的正向代理和反向代理。例如最終的用戶有自己的內網 IP 地址,運營商會分配一個公網 IP,然后訪問某個網站的時候,這個網站可能使用了 CDN 加速一些靜態文件或圖片,如果 CDN 沒有命中,那么就會回源,回源的時候可能還要經過一個反向代理,例如阿里云的 SLB,然后才會到達 Nginx。
我們要拿到的地址應該是運營商給用戶分配的公網 IP 地址 115.204.33.1,對這個 IP 來進行并發連接的控制或者限速,而 Nginx 拿到的卻是 2.2.2.2,那么怎么才能拿到真實的用戶 IP 呢?
HTTP 協議中,有兩個頭部可以用來獲取用戶 IP:
針對這個問題,Nginx 是基于變量來使用。
例如 binary_remote_addr、remote_addr 這樣的變量,其值就是真實的 IP,這樣做連接限制也就是 limit_conn 模塊才有意義,這也說明了,limit_conn 模塊只能在 preaccess 階段,而不能在 postread 階段生效。
Syntax: set_real_ip_from address | CIDR | unix:;
Default: —
Context: http, server, location
Syntax: real_ip_header field | X-Real-IP | X-Forwarded-For | proxy_protocol;
Default: real_ip_header X-Real-IP;
Context: http, server, location
Syntax: real_ip_recursive on | off;
Default: real_ip_recursive off;
Context: http, server, location復制代碼
上面關于 real_ip_recursive 指令可能不太容易理解,我們來實戰練習一下,先來看 real_ip_recursive 默認關閉的情況:
關于如何編譯 Nginx,詳見:iziyang.github.io/2020/03/10/…
# 下載 nginx 源碼,在源碼目錄下執行
./configure --prefix=自己指定的目錄 --with-http_realip_module
make
make install復制代碼
#屏蔽默認的 nginx.conf 文件的 server 塊內容,并添加一行
include /Users/mtdp/myproject/nginx/test_nginx/conf/example/*.conf;復制代碼
# 在 example 目錄下建立 realip.conf,set_real_ip_from 可以設置為自己的本機 IP
server {
listen 80;
server_name ziyang.realip.com;
error_log /Users/mtdp/myproject/nginx/nginx/logs/myerror.log debug;
set_real_ip_from 192.168.0.108;
#real_ip_header X-Real-IP;
real_ip_recursive off;
# real_ip_recursive on;
real_ip_header X-Forwarded-For;
location / {
return 200 "Client real ip: $remote_addr\n";
}
}復制代碼
在上面的配置文件中,我設置了可信代理地址為本機地址,real_ip_recursive 為默認的 off,real_ip_header 設為從 X-Forwarded-For 中取。
./sbin/nginx -s reload復制代碼
? test_nginx curl -H 'X-Forwarded-For: 1.1.1.1,192.168.0.108' ziyang.realip.com
Client real ip: 192.168.0.108復制代碼
然后再來測試 real_ip_recursive 打開的情況:
server {
listen 80;
server_name ziyang.realip.com;
error_log /Users/mtdp/myproject/nginx/nginx/logs/myerror.log debug;
set_real_ip_from 192.168.0.108;
#real_ip_header X-Real-IP;
#real_ip_recursive off;
real_ip_recursive on;
real_ip_header X-Forwarded-For;
location / {
return 200 "Client real ip: $remote_addr\n";
}
}復制代碼
? test_nginx curl -H 'X-Forwarded-For: 1.1.1.1,2.2.2.2,192.168.0.108' ziyang.realip.com
Client real ip: 2.2.2.2復制代碼
所以這里面也可看出來,如果使用 X-Forwarded-For 獲取 realip 的話,需要打開 real_ip_recursive,并且,realip 依賴于 set_real_ip_from 設置的可信地址。
那么有人可能就會問了,那直接用 X-Real-IP 來選取真實的 IP 地址不就好了。這是可以的,但是 X-Real-IP 是 Nginx 獨有的,不是 RFC 規范,如果客戶端與服務器之間還有其他非 Nginx 軟件實現的代理,就會造成取不到 X-Real-IP 頭部,所以這個要根據實際情況來定。
下面來看一下 rewrite 模塊。
首先 rewrite 階段分為兩個,一個是 server_rewrite 階段,一個是 rewrite,這兩個階段都涉及到一個 rewrite 模塊,而在 rewrite 模塊中,有一個 return 指令,遇到該指令就不會再向下執行,直接返回響應。
return 指令的語法如下:
Syntax: return code [text];
return code URL;
return URL;
Default: —
Context: server, location, if復制代碼
返回狀態碼包括以下幾種:
error_page 的作用大家肯定經常見到。當訪問一個網站出現 404 的時候,一般不會直接出現一個 404 NOT FOUND,而是會有一個比較友好的頁面,這就是 error_page 的功能。
Syntax: error_page code ... [=[response]] uri;
Default: —
Context: http, server, location, if in location復制代碼
我們來看幾個例子:
1. error_page 404 /404.html;
2. error_page 500 502 503 504 /50x.html;
3. error_page 404=200 /empty.gif;
4. error_page 404=/404.php;
5. location / {
error_page 404=@fallback;
}
location @fallback {
proxy_pass http://backend;
}
6. error_page 403 http://example.com/forbidden.html;
7. error_page 404=301 http://example.com/notfound.html;復制代碼
那么現在就會有兩個問題,大家看下下面這個配置文件:
server {
server_name ziyang.return.com;
listen 80;
root html/;
error_page 404 /403.html;
#return 405;
location / {
#return 404 "find nothing!";
}
}復制代碼
這兩個問題我們通過實戰驗證一下。
? test_nginx curl ziyang.return.com/text
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>復制代碼
這個時候可以看到,是 error_page 生效了,返回的響應是 403。
那么假如打開了 location 下 return 指令的注釋呢?
? test_nginx curl ziyang.return.com/text
find nothing!% 復制代碼
這時候,return 指令得到了執行。也就是第一個問題,當 server 下包含 error_page 且 location 下有 return 指令的時候,會執行 return 指令。
下面再看一下 server 下的 return 指令和 location 下的 return 指令會執行哪一個。
? test_nginx curl ziyang.return.com/text
<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>復制代碼
針對上面兩個問題也就有了答案:
rewrite 指令用于修改用戶傳入 Nginx 的 URL。來看下 rewrite 的指令規則:
Syntax: rewrite regex replacement [flag];
Default: —
Context: server, location, if復制代碼
它的功能主要有下面幾點:
現在我們有這樣的一個目錄結構:
html/first/
└── 1.txt
html/second/
└── 2.txt
html/third/
└── 3.txt復制代碼
配置文件如下所示:
server {
listen 80;
server_name rewrite.ziyang.com;
rewrite_log on;
error_log logs/rewrite_error.log notice;
root html/;
location /first {
rewrite /first(.*) /second$1 last;
return 200 'first!\n';
}
location /second {
rewrite /second(.*) /third$1;
return 200 'second!\n';
}
location /third {
return 200 'third!\n';
}
location /redirect1 {
rewrite /redirect1(.*) $1 permanent;
}
location /redirect2 {
rewrite /redirect2(.*) $1 redirect;
}
location /redirect3 {
rewrite /redirect3(.*) http://rewrite.ziyang.com$1;
}
location /redirect4 {
rewrite /redirect4(.*) http://rewrite.ziyang.com$1 permanent;
}
} 復制代碼
那么我們的問題是:
帶著這三個問題,我們來實際演示一下。
準備工作
last flag
首先訪問 rewrite.ziyang.com/first/3.txt,結果如下:
? ~ curl rewrite.ziyang.com/first/3.txt
second!復制代碼
為什么結果是 second! 呢?應該是 third! 呀,可能有人會有這樣的疑問。實際的匹配步驟如下:
break flag
下面將 rewrite /second(.*) /third; 這條指令加上 break flag,rewrite /second(.*) /third break;
繼續訪問 rewrite.ziyang.com/first/3.txt,結果如下:
? ~ curl rewrite.ziyang.com/first/3.txt
test3%復制代碼
這時候返回的是 3.txt 文件的內容 test3。實際的匹配步驟如下:
因此,這個過程實際請求的 URL 是 rewrite.ziyang.com/third/3.txt,這樣自然結果就是 test3 了。你還可以試試訪問 rewrite.ziyang.com/third/2.txt 看看會返回什么。
redirect 和 permanent flag
配置文件中還有 4 個 location,你可以分別試著訪問一下,結果是這樣的:
主要是一個指令 rewrite_log:
Syntax: rewrite_log on | off;
Default: rewrite_log off;
Context: http, server, location, if復制代碼
這個指令打開之后,會把 rewrite 的日志寫入 logs/rewrite_error.log 日志文件中,這是請求 /first/3.txt 的日志記錄:
2020/05/06 06:24:05 [notice] 86959#0: *25 "/first(.*)" matches "/first/3.txt", client: 127.0.0.1, server: rewrite.ziyang.com, request: "GET /first/3.txt HTTP/1.1", host: "rewrite.ziyang.com"
2020/05/06 06:24:05 [notice] 86959#0: *25 rewritten data: "/second/3.txt", args: "", client: 127.0.0.1, server: rewrite.ziyang.com, request: "GET /first/3.txt HTTP/1.1", host: "rewrite.ziyang.com"
2020/05/06 06:24:05 [notice] 86959#0: *25 "/second(.*)" matches "/second/3.txt", client: 127.0.0.1, server: rewrite.ziyang.com, request: "GET /first/3.txt HTTP/1.1", host: "rewrite.ziyang.com"
2020/05/06 06:24:05 [notice] 86959#0: *25 rewritten data: "/third/3.txt", args: "", client: 127.0.0.1, server: rewrite.ziyang.com, request: "GET /first/3.txt HTTP/1.1", host: "rewrite.ziyang.com"復制代碼
if 指令也是在 rewrite 階段生效的,它的語法如下所示:
Syntax: if (condition) { ... }
Default: —
Context: server, location復制代碼
它的規則是:
那么 if 指令的條件表達式包含哪些內容呢?它的規則如下:
下面是一些例子:
if ($http_user_agent ~ MSIE) { # 與變量 http_user_agent 匹配
rewrite ^(.*)$ /msie/$1 break;
}
if ($http_cookie ~* "id=([^;]+)(?:;|$)") { # 與變量 http_cookie 匹配
set $id $1;
}
if ($request_method=POST) { # 與變量 request_method 匹配,獲取請求方法
return 405;
}
if ($slow) { # slow 變量在 map 模塊中自定義,也可以進行匹配
limit_rate 10k;
}
if ($invalid_referer) {
return 403;
}復制代碼
當經過 rewrite 模塊,匹配到 URL 之后,就會進入 find_config 階段,開始尋找 URL 對應的 location 配置。
還是老規矩,咱們先來看一下 location 指令的語法:
Syntax: location [=| ~ | ~* | ^~ ] uri { ... }
location @name { ... }
Default: —
Context: server, location
Syntax: merge_slashes on | off;
Default: merge_slashes on;
Context: http, server復制代碼
這里面有一個 merge_slashes 指令,這個指令的作用是,加入 URL 中有兩個重復的 /,那么會合并為一個,這個指令默認是打開的,只有當對 URL 進行 base64 之類的編碼時才需要關閉。
location 的匹配規則是僅匹配 URI,忽略參數,有下面三種大的情況:
對于這些規則剛看上去肯定是很懵的,完全不知道在說什么,下面來實戰看幾個例子。
先看一下 Nginx 的配置文件:
server {
listen 80;
server_name location.ziyang.com;
error_log logs/error.log debug;
#root html/;
default_type text/plain;
merge_slashes off;
location ~ /Test1/$ {
return 200 'first regular expressions match!\n';
}
location ~* /Test1/(\w+)$ {
return 200 'longest regular expressions match!\n';
}
location ^~ /Test1/ {
return 200 'stop regular expressions match!\n';
}
location /Test1/Test2 {
return 200 'longest prefix string match!\n';
}
location /Test1 {
return 200 'prefix string match!\n';
}
location=/Test1 {
return 200 'exact match!\n';
}
}復制代碼
問題就來了,訪問下面幾個 URL 會分別返回什么內容呢?
/Test1
/Test1/
/Test1/Test2
/Test1/Test2/
/test1/Test2復制代碼
例如訪問 /Test1 時,會有幾個部分都匹配上:
訪問 /Test1/ 時,也會有幾個部分匹配上:
那么究竟會匹配哪一個呢?Nginx 其實是遵循一套規則的,如下圖所示:
全部的前綴字符串是放置在一棵二叉樹中的,Nginx 會分為兩部分進行匹配:
下面看下實際的響應是怎么樣的:
? test_nginx curl location.ziyang.com/Test1
exact match!
? test_nginx curl location.ziyang.com/Test1/
stop regular expressions match!
? test_nginx curl location.ziyang.com/Test1/Test2
longest regular expressions match!
? test_nginx curl location.ziyang.com/Test1/Test2/
longest prefix string match!
? test_nginx curl location.ziyang.com/Test1/Test3
stop regular expressions match!復制代碼
這里面重點解釋一下 /Test1/Test3 的匹配過程:
下面就來到了 preaccess 階段。我們經常會遇到一個問題,就是如何限制每個客戶端的并發連接數?如何限制訪問頻率?這些就是在 preaccess 階段處理完成的,顧名思義,preaccess 就是在連接之前。先來看下 limit_conn 模塊。
這里面涉及到的模塊是 ngx_http_limit_conn_module,它的基本特性如下:
這里面有一點需要注意,就是 limit_conn key 的設計,所謂的 key 指的就是對哪個變量進行限制,通常我們取的都是用戶的真實 IP。
說完了 limit_conn 的模塊,再來說一下指令語法。
Syntax: limit_conn_zone key zone=name:size;
Default: —
Context: http復制代碼
Syntax: limit_conn zone number;
Default: —
Context: http, server, location復制代碼
Syntax: limit_conn_log_level info | notice | warn | error;
Default: limit_conn_log_level error;
Context: http, server, location復制代碼
Syntax: limit_conn_status code;
Default: limit_conn_status 503;
Context: http, server, location復制代碼
下面又到了實戰的環節了,通過一個實際的例子來看一下以上的幾個指令是怎么起作用的。
老規矩,先上配置文件:
limit_conn_zone $binary_remote_addr zone=addr:10m;
#limit_req_zone $binary_remote_addr zone=one:10m rate=2r/m;
server {
listen 80;
server_name limit.ziyang.com;
root html/;
error_log logs/myerror.log info;
location /{
limit_conn_status 500;
limit_conn_log_level warn;
limit_rate 50;
limit_conn addr 1;
#limit_req zone=one burst=3 nodelay;
#limit_req zone=one;
}
}復制代碼
在這個配置文件中,做了兩條限制,一個是 limit_rate 限制為 50 個字節,并發連接數 limit_conn 限制為 1。
? test_nginx curl limit.ziyang.com復制代碼
這時候訪問 limit.ziyang.com 這個站點,會發現速度非常慢,因為每秒鐘只有 50 個字節。
如果再同時訪問這個站點的話,則會返回 500。
我在另一個終端里面同時訪問:
? ~ curl limit.ziyang.com
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>復制代碼
可以看到,Nginx 直接返回了 500。
在本節開頭我們就提出了兩個問題:
第一個問題限制并發連接數的問題已經解決了,下面來看第二個問題。
這里面生效的模塊是 ngx_http_limit_req_module,它的基本特性如下:
leaky bucket 叫漏桶算法,其他用來限制請求速率的還有令牌環算法等,這里面不展開講。
漏桶算法的原理是,先定義一個桶的大小,所有進入桶內的請求都會以恒定的速率被處理,如果請求太多超出了桶的容量,那么就會立刻返回錯誤。用一張圖解釋一下。
這張圖里面,水龍頭在不停地滴水,就像用戶發來的請求,所有的水滴都會以恒定的速率流出去,也就是被處理。漏桶算法對于突發流量有很好的限制作用,會將所有的請求平滑的處理掉。
Syntax: limit_req_zone key zone=name:size rate=rate ;
Default: —
Context: http復制代碼
rate 單位為 r/s 或者 r/m(每分鐘或者每秒處理多少個請求)
Syntax: limit_req zone=name [burst=number] [nodelay];
Default: —
Context: http, server, location復制代碼
burst 默認為 0 nodelay,如果設置了這個參數,那么對于漏桶中的請求也會立刻返回錯誤
Syntax: limit_req_log_level info | notice | warn | error;
Default: limit_req_log_level error;
Context: http, server, location復制代碼
Syntax: limit_req_status code;
Default: limit_req_status 503;
Context: http, server, location復制代碼
在實際驗證之前呢,需要注意兩個問題:
添加配置文件,這個配置文件與上一節的配置文件其實是相同的只不過需要注釋一下:
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=one:10m rate=2r/m;
server {
listen 80;
server_name limit.ziyang.com;
root html/;
error_log logs/myerror.log info;
location /{
limit_conn_status 500;
limit_conn_log_level warn;
#limit_rate 50;
#limit_conn addr 1;
#limit_req zone=one burst=3 nodelay;
limit_req zone=one;
}
}復制代碼
結論:在 limit_req zone=one 指令下,超出每分鐘處理的請求數后就會立刻返回 503。
? test_nginx curl limit.ziyang.com
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>復制代碼
改變一下注釋的指令:
limit_req zone=one burst=3;
#limit_req zone=one;復制代碼
在沒有添加 burst 參數時,會立刻返回錯誤,而加上之后,不會返回錯誤,而是等待請求限制解除,直到可以處理請求時再返回。
再來看一下 nodelay 參數:
limit_req zone=one burst=3 nodelay;復制代碼
添加了 nodelay 之后,請求在沒有達到 burst 限制之前都可以立刻被處理并返回,超出了 burst 限制之后,才會返回 503。
現在可以回答一下剛開始提出的兩個問題:
經過 preaccess 階段對用戶的限流之后,就到了 access 階段。
這里面涉及到的模塊是 ngx_http_access_module,它的基本特性如下:
Syntax: allow address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except
Syntax: deny address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except復制代碼
access 模塊提供了兩條指令 allow 和 deny,來看幾個例子:
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
deny all;
}復制代碼
對于用戶訪問來說,這些指令是順序執行的,當滿足了一條之后,就不會再向下執行。這個模塊比較簡單,我們這里不做實戰演練了。
auth_basic 模塊是用作用戶認證的,當開啟了這個模塊之后,我們通過瀏覽器訪問網站時,就會返回一個 401 Unauthorized,當然這個 401 用戶不會看見,瀏覽器會彈出一個對話框要求輸入用戶名和密碼。這個模塊使用的是 RFC2617 中的定義。
Syntax: auth_basic string | off;
Default: auth_basic off;
Context: http, server, location, limit_except
Syntax: auth_basic_user_file file;
Default: —
Context: http, server, location, limit_except復制代碼
這里面我們會用到一個工具叫 htpasswd,這個工具可以用來生成密碼文件,而 auth_basic_user_file 就依賴這個密碼文件。
htpasswd 依賴安裝包 httpd-tools
生成密碼的命令為:
htpasswd –c file –b user pass復制代碼
生成的密碼文件的格式為:
# comment
name1:password1
name2:password2:comment
name3:password3復制代碼
htpasswd -bc auth.pass ziyang 123456復制代碼
server {
server_name access.ziyang.com;
listen 80;
error_log logs/error.log debug;
default_type text/plain;
location /auth_basic {
satisfy any;
auth_basic "test auth_basic";
auth_basic_user_file example/auth.pass;
deny all;
}
}復制代碼
這時候訪問 access.ziyang.com 就會彈出對話框,提示輸入密碼:
Syntax: auth_request uri | off;
Default: auth_request off;
Context: http, server, location
Syntax: auth_request_set $variable value;
Default: —
Context: http, server, location復制代碼
server {
server_name access.ziyang.com;
listen 80;
error_log logs/error.log debug;
#root html/;
default_type text/plain;
location /auth_basic {
satisfy any;
auth_basic "test auth_basic";
auth_basic_user_file example/auth.pass;
deny all;
}
location / {
auth_request /test_auth;
}
location=/test_auth {
proxy_pass http://127.0.0.1:8090/auth_upstream;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}復制代碼
Syntax: satisfy all | any;
Default: satisfy all;
Context: http, server, location復制代碼
satisfy 指令有兩個值一個是 all,一個是 any,這個模塊對 acces 階段的三個模塊都生效:
如果 satisfy 指令的值是 all 的話,就表示必須所有 access 階段的模塊都要執行,都通過了才會放行;值是 any 的話,表示有任意一個模塊得到執行即可。
下面有幾個問題可以加深一下理解:
講到了這里,我們再來回顧一下 Nginx 處理 HTTP 請求的 11 個階段:
現在我們已經來到了 precontent 階段,這個階段只有 try_files 這一個指令。
Syntax: try_files file ... uri;
try_files file ...=code;
Default: —
Context: server, location復制代碼
下面我們實際看一個例子:
server {
server_name tryfiles.ziyang.com;
listen 80;
error_log logs/myerror.log info;
root html/;
default_type text/plain;
location /first {
try_files /system/maintenance.html
$uri $uri/index.html $uri.html
@lasturl;
}
location @lasturl {
return 200 'lasturl!\n';
}
location /second {
try_files $uri $uri/index.html $uri.html=404;
}
}復制代碼
結果如下:
這兩個結果都與配置文件是一致的。
? test_nginx curl tryfiles.ziyang.com/second
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
? test_nginx curl tryfiles.ziyang.com/first
lasturl!復制代碼
mirror 模塊可以實時拷貝流量,這對于需要同時訪問多個環境的請求是非常有用的。
Syntax: mirror uri | off;
Default: mirror off;
Context: http, server, location
Syntax: mirror_request_body on | off;
Default: mirror_request_body on;
Context: http, server, location復制代碼
server {
server_name mirror.ziyang.com;
listen 8001;
error_log logs/error_log debug;
location / {
mirror /mirror;
mirror_request_body off;
}
location=/mirror {
internal;
proxy_pass http://127.0.0.1:10020$request_uri;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}復制代碼
下面開始就到了 content 階段,先來看 content 階段的 static 模塊,雖然這是位于 content 階段的最后一個處理模塊,但是這里先來介紹它。
static 模塊
先來一下 root 和 alias 這兩個指令,這兩個指令都是用來映射文件路徑的。
Syntax: alias path;
Default: —
Context: location復制代碼
Syntax: root path;
Default: root html;
Context: http, server, location, if in location復制代碼
下面來看一個問題:
現在有一個文件路徑:
html/first/
└── 1.txt復制代碼
配置文件如下所示:
server {
server_name static.ziyang.com;
listen 80;
error_log logs/myerror.log info;
location /root {
root html;
}
location /alias {
alias html;
}
location ~ /root/(\w+\.txt) {
root html/first/$1;
}
location ~ /alias/(\w+\.txt) {
alias html/first/$1;
}
location /RealPath/ {
alias html/realpath/;
return 200 '$request_filename:$document_root:$realpath_root\n';
}
}復制代碼
那么訪問以下 URL 會得到什么響應呢?
/root
/alias
/root/1.txt
/alias/1.txt復制代碼
? test_nginx curl static.ziyang.com/alias/1.txt
test1%
? test_nginx curl static.ziyang.com/alias/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
? test_nginx curl static.ziyang.com/root/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
? test_nginx curl static.ziyang.com/root/1.txt
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>復制代碼
訪問這四個路徑分別得到的結果是:
這是為什么呢?是因為,root 在映射 URL 時,會把 location 中的路徑也加進去,也就是:
還是上面的配置文件:
location /RealPath/ {
alias html/realpath/;
return 200 '$request_filename:$document_root:$realpath_root\n';
}復制代碼
這里有一個問題,在訪問 /RealPath/1.txt 時,這三個變量的值各為多少?
為了解答這個問題,我們先來解釋三個變量:
為了驗證這三個變量,在 html 目錄下建立一個軟鏈接指向 first 文件夾:
ln -s first realpath復制代碼
? html curl static.ziyang.com/realpath/1.txt
/Users/mtdp/myproject/nginx/test_nginx/html/realpath/1.txt:/Users/mtdp/myproject/nginx/test_nginx/html/realpath/:/Users/mtdp/myproject/nginx/test_nginx/html/first復制代碼
可以看出來,三個路徑分別是:
還有其他的一些配置指令,例如:
靜態文件返回時的 Content-Type
Syntax: types { ... }
Default: types { text/html html; image/gif gif; image/jpeg jpg; }
Context: http, server, location
Syntax: default_type mime-type;
Default: default_type text/plain;
Context: http, server, location
Syntax: types_hash_bucket_size size;
Default: types_hash_bucket_size 64;
Context: http, server, location
Syntax: types_hash_max_size size;
Default: types_hash_max_size 1024;
Context: http, server, location復制代碼
未找到文件時的錯誤日志
Syntax: log_not_found on | off;
Default: log_not_found on;
Context: http, server, location復制代碼
在生產環境中,經常可能會有找不到文件的情況,錯誤日志中就會打印出來:
[error] 10156#0: *10723 open() "/html/first/2.txt/root/2.txt" failed (2: No such file or directory)復制代碼
如果不想記錄日志,可以關掉。
現在有另外一個問題,當我們訪問目錄時最后沒有帶 /,static 模塊會返回 301 重定向,那么這個規則是怎么定義的呢,看下面三個指令:
# 該指令決定重定向時的域名,可以決定返回哪個域名
Syntax: server_name_in_redirect on | off;
Default: server_name_in_redirect off;
Context: http, server, location
# 該指令決定重定向時的端口
Syntax: port_in_redirect on | off;
Default: port_in_redirect on;
Context: http, server, location
# 該指令決定是否填域名,默認是打開的,也就是返回絕對路徑
Syntax: absolute_redirect on | off;
Default: absolute_redirect on;
Context: http, server, location復制代碼
這三個指令的實際用法來實戰演示一下,先來看配置文件:
server {
server_name return.ziyang.com dir.ziyang.com;
server_name_in_redirect on;
listen 8088;
port_in_redirect on;
absolute_redirect off;
root html/;
}復制代碼
absolute_redirect 默認是打開的,我們把它關閉了,看下是怎么返回的:
? test_nginx curl localhost:8088/first -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.17.8
Date: Tue, 12 May 2020 00:31:36 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: /first/復制代碼
這個時候看到返回的頭部 Location 中沒有加上域名。
下面再把 absolute_redirect 打開(默認是打開的,因此注釋掉就行了),看下返回什么:
? test_nginx curl localhost:8088/first -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.17.8
Date: Tue, 12 May 2020 00:35:49 GMT
Content-Type: text/html
Content-Length: 169
Location: http://return.ziyang.com:8088/first/
Connection: keep-alive復制代碼
可以看到,這時候就返回了域名,而且返回的是我們配置的主域名加端口號,這是因為,server_name_in_redirect 和 port_in_redirect 這兩個指令打開了,如果關閉掉這兩個指令,看下返回什么:
? test_nginx curl localhost:8088/first -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.17.8
Date: Tue, 12 May 2020 00:39:31 GMT
Content-Type: text/html
Content-Length: 169
Location: http://localhost/first/
Connection: keep-alive復制代碼
這兩個指令都設置為 off 之后,會發現返回的不再是主域名加端口號,而是我們請求的域名和端口號,如果在請求頭中加上 Host,那么就會用 Host 請求頭中的域名。
這個模塊,當我們訪問以 / 結尾的目錄時,會去找 root 或 alias 指令的文件夾下的 index.html,如果有這個文件,就會把文件內容返回,也可以指定其他文件。
server {
server_name autoindex.ziyang.com;
listen 8080;
location / {
alias html/;
autoindex on;
#index b.html;
autoindex_exact_size on;
autoindex_format html;
autoindex_localtime on;
}
}復制代碼
這里我把 index b.html 這條指令給注釋掉了,而 index 模塊是默認編譯進 Nginx 的,且默認指令是 index index.html,因此,會去找是否有 index.html 這個文件。
后面的文件大小顯示格式就是由 autoindex_exact_size on; 這條指令決定的。
下面介紹一個可以提升小文件性能的模塊,這個模塊是由阿里巴巴開發的,在淘寶網中有廣泛應用。
打開淘寶主頁,會發現小文件都是通過這個模塊來提高性能的:
這里就不做實戰了,感興趣的同學可以自己去編譯一下這個模塊,做一下實驗,我把配置文件放在這里:
server {
server_name concat.ziyang.com;
error_log logs/myerror.log debug;
concat on;
root html;
location /concat {
concat_max_files 20;
concat_types text/plain;
concat_unique on;
concat_delimiter ':::';
concat_ignore_file_error on;
}
}復制代碼
下面終于來到了 11 個階段的最后一個階段,記錄請求訪問日志的 log 模塊。
Syntax: log_format name [escape=default|json|none] string ...;
Default: log_format combined "...";
Context: http復制代碼
默認的 combined 日志格式:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent ' '"$http_referer"
"$http_user_agent"';復制代碼
Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
Default: access_log logs/access.log combined;
Context: http, server, location, if in location, limit_except復制代碼
Syntax: open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
Default: open_log_file_cache off;
Context: http, server, location復制代碼
日志模塊沒有實戰。
到了這里,我們已經將 Nginx 處理 HTTP 請求的 11 個階段全部梳理了一遍,每個階段基本都有對應的模塊。相信對于這樣一個全流程的解析,大家都能夠看懂 Nginx 的配置了,在此之上,還能夠按照需求靈活配置出自己想要的配置,這樣就真正的掌握了 11 個階段。
ordPress在不做任何優化的情況下,網站加載的速度實在不敢恭維,最好的優化方式就是給WordPress做一個緩存,前面的文章為大家提供了兩種WordPress緩存方案,一個是memcached+batcache,一個是redis,這兩種緩存任選一個就足以讓你的WordPress站點秒加載,感興趣的可以看下
而本篇文章講的就是另一個緩存,那就是NGINX-Fastcgi_cache,直接由NGINX來緩存你的網站頁面,并且支持偽靜態頁面,效率更高
寶塔已經默認編譯了這個模塊,我們可以輸入nginx -V來查詢是否編譯了這個模塊,看到ngx_cache_purge就說明是編譯成功了的
如果沒有使用寶塔就需要到官方網站:http://labs.frickle.com/files/ 里下載最新的ngx_cache_purge模塊自行編譯
需要在根目錄(服務器根目錄)的tmp文件夾里創建wpcache文件夾,用作存儲網站緩存的目錄
路徑:/tmp/wpcache
我們需要在站點vhost文件里添加一下我們的緩存配置
將以下代碼加在server前
#下面各個參數的含義請自行百度,我就不贅述了
#下面 2 行的中的 wpcache 路徑請自行提前創建,否則可能會路徑不存在而無法啟動 nginx,max_size 請根據分區大小自行設置
fastcgi_cache_path /tmp/wpcache levels=1:2 keys_zone=WORDPRESS:250m inactive=1d max_size=1G;
fastcgi_temp_path /tmp/wpcache/temp;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
#忽略一切 nocache 申明,避免不緩存偽靜態等
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
#Ps:如果是多個站點,以上內容不要重復添加,否則會沖突,可以考慮將以上內容添加到 nginx.conf 里面,避免加了多次。
將以下代碼加入#SSL-END下面
#post 訪問不緩存
if ($request_method=POST) {
set $skip_cache 1;
}
#動態查詢不緩存
if ($query_string !="") {
set $skip_cache 1;
}
#后臺等特定頁面不緩存(其他需求請自行添加即可)
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
set $skip_cache 1;
}
#對登錄用戶、評論過的用戶不展示緩存(這個規則張戈博客并沒有使用,所有人看到的都是緩存)
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $skip_cache 1;
}
#這里請參考你網站之前的配置,特別是 sock 的路徑,弄錯了就 502 了!
location ~ [^/]\.php(/|$)
{
try_files $uri=404;
fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
#新增的緩存規則
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache "$upstream_cache_status From $host";
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 1d;
}
#緩存清理配置(可選模塊,請細看下文說明)
location ~ /purge(/.*) {
allow 127.0.0.1;
allow "此處填寫你服務器的真實外網 IP";
deny all;
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
location=/robots.txt { access_log off; log_not_found off; }
location ~ /\. { deny all; access_log off; log_not_found off; }
寶塔面板的sock文件在/www/server/php/你的php版本/etc/php-fpm.conf文件中可看到sock文件路徑
WordPress安裝Nginx Helper插件
這個插件可以和fastcgi緩存配合使用,清理緩存更方便,但是插件沒有中文翻譯,大家可以用瀏覽器翻譯查看,或者對照下面我標注的插件界面使用
緩存清理選項建議大家使用本地清理模式,因為用purge清理方式會產生請求
啟用Enable Nginx Timestamp in HTML這個插入緩存信息以后,打開你的緩存頁面查看源代碼底部將會有類似以下信息
<!--Cached using Nginx-Helper on 2023-08-10 13:59:57. It took 1 queries executed in 0.161 seconds.-->
由于插件默認的緩存路徑為 /var/run/nginx-cache ,它找不到我們設置的路徑,所以插件清理緩存的效果就沒有用,我們需要在wp-config.php中加入以下代碼定義插件緩存路徑即可
//根據實際情況定義緩存的存放路徑
define( 'RT_WP_NGINX_HELPER_CACHE_PATH','/tmp/wpcache');
不知道放在哪里的話就放在define( 'WP_DEBUG', false );下方即可
成功啟用緩存效果后我們查看網站header信息會有一個x-cache的信息,x-cache有3種狀態
緩存清理直接到Nginx Helper插件處點擊紅按鈕Purge Entire Cache即可,如果清不掉可能是前面叫加在wp-config.php里的代碼漏加了,也可以直接把wpcache文件夾里的文件全部刪除也可清空緩存。
有的CDN接入后x-cache會一直顯示BYBASS狀態,不用擔心,這是正常的,緩存依然是生效的
直接將以下代碼加入nginx的vhost文件
#站點 1 緩存配置
fastcgi_cache_path /tmp/a_cache levels=1:2 keys_zone=www.a.com:384m inactive=1d max_size=5G;
#站點 2 緩存配置
#如果要開啟更多站點緩存,請繼續增加,注意每個站點的 緩存路徑 和 keys_zone 要自定義區分一下
#Ps:代碼中的參數都只是范例,實際使用請根據服務器配置自行修改
fastcgi_cache_path /tmp/b_cache levels=1:2 keys_zone=www.b.com:384m inactive=1d max_size=5G;
#其他配置可以不變
fastcgi_temp_path /tmp/temp_cache;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
要將站點vhost文件中含以上的代碼剔除掉再添加,文件夾同樣需要自行添加
下面是兩個示例站點的vhost文件示例,把fastcgi_cache后面的值填為自己的網站域名即可
server(
#其他配置略
location ~ [^/]\.php(/|$)
{
try_files $uri=404;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
#fastcgi 緩存配置
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache "$upstream_cache_status From $host";
fastcgi_cache www.a.com;
fastcgi_cache_valid 200 301 302 1d;
}
#其他配置略
}
server(
#以上配置略
location ~ [^/]\.php(/|$)
{
try_files $uri=404;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
#fastcgi 緩存配置
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache "$upstream_cache_status From $host";
fastcgi_cache www.b.com;
fastcgi_cache_valid 200 301 302 1d;
}
#以下配置略
}
這樣配置多站點配置fastcgi緩存就沒問題了
原文鏈接:HongNote - WordPress開啟NGINX-Fastcgi_cache緩存
者:小不點啊
來源:www.cnblogs.com/leeSmall/p/9356535.html
一、Nginx Rewrite 規則
Rewrite規則含義就是某個URL重寫成特定的URL(類似于Redirect),從某種意義上說為了美觀或者對搜索引擎友好,提高收錄量及排名等。
語法:
rewrite <regex> <replacement> [flag]
關鍵字 || 正則 || 替代內容 || flag標記
Rewrite規則的flag標記主要有以下幾種:
last和break用來實現URL重寫,瀏覽器地址欄URL地址不變
a) 例如用戶訪問www.dbspread.com,想直接跳轉到網站下面的某個頁面,www.dbspread.com/new.index.html如何來實現呢?我們可以使用Nginx Rewrite 來實現這個需求,具體如下:在server中加入如下語句即可:
效果圖如下:
rewrite ^/$ http://www.dbspread.com/new.index.html permanent;
對應如下語法:
rewrite <regex> <replacement> [flag];
關鍵字 正則 替代內容 flag標記
正則表達式說明:
*代表前面0或更多個字符 +代表前面1或更多個字符
?代表前面0或1個字符 ^代表字符串的開始位置
$代表字符串結束的位置 。為通配符,代表任何字符
b)例如多個域名跳轉到同一個域名,nginx rewrite規則寫法如下:
格式:
rewrite <regex> <replacement> [flag];
關鍵字 || 正則 || 替代內容 || flag標記
說明:
比如http://www.dbspread.com/download/av123.rmvb 這個視頻下載地址被其他網站引用,比如在www.test.com的index.html引用download/av123.rmvb就叫盜鏈,我們要禁止這種引用就叫做防盜鏈
在nginx的nginx.conf的server里面配置如下代碼
三、Nginx 動靜分離
Nginx動靜分離是讓動態網站里的動態網頁根據一定規則把不變的資源和經常變的資源區分開來,動靜資源做好了拆分以后,我們就可以根據靜態資源的特點將其做緩存操作,這就是網站靜態化處理的核心思路。
1). WEB項目開發時要注意,將靜態資源盡量放在一個static文件夾2). 將static靜態資源文件夾放到Nginx可以取到的位置3). 頁面要建立全局變量路徑,方便修改路徑4). 修改nginx.conf的location, 匹配靜態資源請求
body {
margin: 10px 20px;
text-align: center;
font-family: Arial, sans-serif;
background-color: red;
}
4.4 在nginx的nginx.conf中server節點新增靜態資源分離的配置
對于Nginx基礎配置,推薦之前的:后端實踐:Nginx日志配置(超詳細)
4.5 訪問頁面查看效果
Keepalived軟件起初是專為LVS負載均衡軟件設計的,用來管理并監控LVS集群系統中各個服務節點的狀態,后來又加入了可以實現高可用的VRRP (Virtual Router Redundancy Protocol ,虛擬路由器冗余協議)功能。因此,Keepalived除了能夠管理LVS軟件外,還可以作為其他服務(例如:Nginx、Haproxy、MySQL等)的高可用解決方案軟件
管理LVS負載均衡軟件實現LVS集群節點的健康檢查作為系統網絡服務的高可用性(failover)
Keepalived高可用服務之間的故障切換轉移,是通過 VRRP 來實現的。在 Keepalived服務正常工作時,主 Master節點會不斷地向備節點發送(多播的方式)心跳消息,用以告訴備Backup節點自己還活著,當主 Master節點發生故障時,就無法發送心跳消息,備節點也就因此無法繼續檢測到來自主 Master節點的心跳了,于是調用自身的接管程序,接管主Master節點的 IP資源及服務。而當主 Master節點恢復時,備Backup節點又會釋放主節點故障時自身接管的IP資源及服務,恢復到原來的備用角色。
說明:keepalived的主從切換和redis的主從切換是不一樣的,keepalived的主節點掛了以后,從節點變為主節點,之前的主節點恢復以后繼續做主節點。redis的主節點掛了以后,重新恢復以后變為從節點
說明:
虛擬ip(VIP):192.168.152.200,對外提供服務的ip,也可稱作浮動ip192.168.152.130:nginx + keepalived master 主192.168.152.129:nginx + keepalived backup 從192.168.152.129:tomcat-8080192.168.152.129:tomcat-8081
環境準備:
centos6、jdk
虛擬ip(VIP):192.168.152.200,對外提供服務的ip,也可稱作浮動ip
192.168.152.130:nginx + keepalived master 主
192.168.152.129:nginx + keepalived backup 從
192.168.152.129:tomcat-8080
192.168.152.129:tomcat-8081
nginx和tomcat的環境準備請查看我的前一篇關于nginx的文章
注:192.168.152.129(keepalived從節點) 與 192.168.152.130(keepalived主節點)先安裝好nginx + keepalived
下載壓縮包:
wget www.keepalived.org/software/keepalived-1.3.5.tar.gz
解壓縮:
tar -zxvf keepalived-1.3.5.tar.gz
進入解壓縮以后的文件目錄:
cd keepalived-1.3.5
編譯安裝:./configure --prefix=/usr/local/keepalived系統提示警告 *** WARNING - this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.yum -y install libnl libnl-devel再次執行./configure --prefix=/usr/local/keepalived系統提示錯誤 configure: error: libnfnetlink headers missingyum install -y libnfnetlink-devel再次執行./configure --prefix=/usr/local/keepalived
make && make install
到此keepalived安裝完成,但是接下來還有最關鍵的一步,如果這一步沒有做后面啟動keepalived的時候會報找不到配置文件的錯誤
Configuration file '/etc/keepalived/keepalived.conf' is not a regular non-executable file
安裝完成后,進入安裝目錄的etc目錄下,將keepalived相應的配置文件拷貝到系統相應的目錄當中。keepalived啟動時會從/etc/keepalived目錄下查找keepalived.conf配置文件
mkdir /etc/keepalived
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived
5.3 修改keepalived從節點192.168.152.129的/etc/keepalived/keepalived.conf配置文件
5.4 檢查nginx是否啟動的shell腳本
/usr/local/src/check_nginx_pid.sh
#!/bin/bash
#檢測nginx是否啟動了
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then #如果nginx沒有啟動就啟動nginx
/usr/local/nginx/sbin/nginx #重啟nginx
if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then #nginx重啟失敗,則停掉keepalived服務,進行VIP轉移
killall keepalived
fi
fi
5.5 192.168.152.130(keepalived主節點)和 192.168.152.129(keepalived從節點)的nginx的配置文件nginx.conf
user root root; #使用什么用戶啟動NGINX 在運行時使用哪個用戶哪個組
worker_processes 4; #啟動進程數,一般是1或8個,根據你的電腦CPU數,一般8個
worker_cpu_affinity 00000001 00000010 00000100 00001000; #CPU邏輯數——把每個進程分別綁在CPU上面,為每個進程分配一個CPU
#pid /usr/local/nginx/logs/nginx.pid
worker_rlimit_nofile 102400; #一個進程打開的最大文件數目,與NGINX并發連接有關系
#工作模式及連接數上限
events
{
use epoll; #多路復用IO 基于LINUX2.6以上內核,可以大大提高NGINX的性能 uname -a查看內核版本號
worker_connections 102400; #單個worker process最大連接數,其中NGINX最大連接數=連接數*進程數,一般1GB內存的機器上可以打開的最大數大約是10萬左右
multi_accept on; #盡可能多的接受請求,默認是關閉狀態
}
#處理http請求的一個應用配置段
http
{
#引用mime.types,這個類型定義了很多,當web服務器收到靜態的資源文件請求時,依據請求文件的后綴名在服務器的MIME配置文件中找到對應的MIME #Type,根據MIMETYPE設置并response響應類型(Content-type)
include mime.types;
default_type application/octet-stream; #定義的數據流,有的時候默認類型可以指定為text,這跟我們的網頁發布還是資源下載是有關系的
fastcgi_intercept_errors on; #表示接收fastcgi輸出的http 1.0 response code
charset utf-8;
server_names_hash_bucket_size 128; #保存服務器名字的hash表
#用來緩存請求頭信息的,容量4K,如果header頭信息請求超過了,nginx會直接返回400錯誤,先根據client_header_buffer_size配置的值分配一個buffer,如果##分配的buffer無法容納request_line/request_header,那么就會##再次根據large_client_header_buffers配置的參數分配large_buffer,如果large_buffer還是無#法容納,那么就會返回414(處理request_line)/400(處理request_header)錯誤。
client_header_buffer_size 4k;
large_client_header_buffers 4 32k;
client_max_body_size 300m; #允許客戶端請求的最大單文件字節數 上傳文件時根據需求設置這個參數
#指定NGINX是否調用這個函數來輸出文件,對于普通的文件我們必須設置為ON,如果NGINX專門做為一個下載端的話可以關掉,好處是降低磁盤與網絡的IO處理數及#系統的UPTIME
sendfile on;
#autoindex on;開啟目錄列表訪問,適合下載服務器
tcp_nopush on; #防止網絡阻塞
#非常重要,根據實際情況設置值,超時時間,客戶端到服務端的連接持續有效時間,60秒內可避免重新建立連接,時間也不能設太長,太長的話,若請求數10000##,都占用連接會把服務托死
keepalive_timeout 60;
tcp_nodelay on; #提高數據的實時響應性
client_body_buffer_size 512k; #緩沖區代理緩沖用戶端請求的最大字節數(請求多)
proxy_connect_timeout 5; #nginx跟后端服務器連接超時時間(代理連接超時)
proxy_read_timeout 60; #連接成功后,后端服務器響應時間(代理接收超時)
proxy_send_timeout 5; #后端服務器數據回傳時間(代理發送超時)
proxy_buffer_size 16k; #設置代理服務器(nginx)保存用戶頭信息的緩沖區大小
proxy_buffers 4 64k; #proxy_buffers緩沖區,網頁平均在32k以下的話,這樣設置
proxy_busy_buffers_size 128k; #高負荷下緩沖大小
proxy_temp_file_write_size 128k; #設定緩存文件夾大小,大于這個值,將從upstream服務器傳
gzip on; #NGINX可以壓縮靜態資源,比如我的靜態資源有10M,壓縮后只有2M,那么瀏覽器下載的就少了
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2; #壓縮級別大小,最小1,最大9.值越小,壓縮后比例越小,CPU處理更快,為1時,原10M壓縮完后8M,但設為9時,壓縮完可能只有2M了。一般設置為2
gzip_types text/plain application/x-javascript text/css application/xml; #壓縮類型:text,js css xml 都會被壓縮
gzip_vary on; #作用是在http響應中增加一行目的是改變反向代理服務器的緩存策略
#日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' #ip 遠程用戶 當地時間 請求URL
'$status $body_bytes_sent "$http_referer" ' #狀態 發送的大小 響應的頭
'"$http_user_agent" $request_time'; #客戶端使用的瀏覽器 頁面響應的時間
#動態轉發
upstream web1 {
#每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決session的問題。配置了ip_hash就沒有負載均衡的效果了,每次訪問的都是同一個tomcat
#ip_hash;
#轉發的后端的tomcat服務器,weight表示轉發的權重,越大轉發的次數越多,機器性能不一樣配置的weight值不一樣
server 192.168.152.129:8080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.152.129:8081 weight=1 max_fails=2 fail_timeout=30s;
}
upstream web2 {
server 192.168.152.129:8090 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.152.129:8091 weight=1 max_fails=2 fail_timeout=30s;
}
server {
listen 80; #監聽80端口
server_name www.dbspread.com; #域名
#rewrite規則
index index.jsp index.html index.htm;
root /usr/local/nginx/html; #定義服務器的默認網站根目錄位置
#重定向
if ($host != 'www.dbspread.com' ){
rewrite ^/(.*)$ http://www.dbspread.com/$1 permanent;
}
#防盜鏈
location ~* \.(rmvb|jpg|png|swf|flv)$ { #rmvb|jpg|png|swf|flv表示對rmvb|jpg|png|swf|flv后綴的文件實行防盜鏈
valid_referers none blocked www.dbspread.com; #表示對www.dbspread.com此域名開通白名單,比如在www.test.com的index.html引用download/av123.rmvb,無效
root html/b;
if ($invalid_referer) { #如果請求不是從www.dbspread.com白名單發出來的請求,直接重定向到403.html這個頁面或者返回403
#rewrite ^/ http://www.dbspread.com/403.html;
return 403;
}
}
#監聽完成以后通過斜桿(/)攔截請求轉發到后端的tomcat服務器
location /
{
#如果后端的服務器返回502、504、執行超時等錯誤,自動將請求轉發到upstream負載均衡池中的另一臺服務器,實現故障轉移。
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_set_header Host $host; #獲取客戶端的主機名存到變量Host里面,從而讓tomcat取到客戶端機器的信息
proxy_set_header X-Real-IP $remote_addr; #獲取客戶端的主機名存到變量X-Real-IP里面,從而讓tomcat取到客戶端機器的信息
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#rewrite ^/$ http://www.dbspread.com/new.index.html permanent;#用戶訪問www.dbspread.com,想直接跳轉到網站下面的某個頁面:www.dbspread.com/new.index.html
proxy_pass http://web1; #跳轉到對應的應用web1
}
# location ~ .*\.(php|jsp|cgi|shtml)?$ #動態分離 ~匹配 以.*結尾(以PHP JSP結尾走這段)
# {
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_pass http://jvm_web2;
# }
#靜態分離 ~匹配 以.*結尾(以html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css結尾走這段),當然不是越久越好,如果有10000個用戶在線,都保存幾個月,系統托跨
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{
root /var/local/static; #靜態資源存放在nginx的安裝機器上
#proxy_pass http://www.static.com; #靜態資源也可存放在遠程服務器上
expires 30d;
}
#日志級別有[debug|info|notice|warn|error|crit] error_log 級別分為 debug, info, notice, warn, error, crit 默認為crit, 生產環境用error
#crit 記錄的日志最少,而debug記錄的日志最多
access_log /usr/local/logs/web2/access.log main;
error_log /usr/local/logs/web2/error.log crit;
}
}
到這一步環境準備已完成,相關的配置也修改完成,下面我們來查看效果
5.6 配置hosts域名映射
192.168.152.200 www.dbspread.com
注意:這里192.168.152.200 是keepalived里面virtual_ipaddress配置的虛擬ip
virtual_ipaddress {
192.168.152.200 # 定義虛擬ip(VIP),可多設,每行一個
}
到這一步環境準備已完成,相關的配置也修改完成,下面我們來查看效果
5.7 分別啟動192.168.152.129的兩個tomcat
5.8 分別啟動192.168.152.130(keepalived主節點)和
192.168.152.129(keepalived從節點)的keepalived的
啟動命令:
/usr/local/keepalived/sbin/keepalived
可以看到keepalived和nginx都啟動了
在瀏覽器輸入www.dpspread.com域名訪問
可以看到從節點變為主節點了
在瀏覽器輸入地址www.dpspread.com訪問,可以看到訪問正常
可以看到主節點重新啟動以后變為主節點了
之前變為主節點的從節點又變回從節點了
*請認真填寫需求信息,我們會在24小時內與您取得聯系。