當讓內網用戶通過一個有外網地址的網關訪問互聯網時,內網主機的網關都指向其中有外網的主機的內網地址,一般在網關處添加一個snat實現,內網所有客戶端只要訪問外網,經由網關時發送時都將原地址改為這個網關的外網IP地址;從而實現內網用戶訪問互聯網,這種轉發內核級完成,沒有用戶參與;
還有第二種方式實現內網用戶訪問外網:
正向代理
讓內網用戶的網關可指向一個有外網地址的網關訪問互聯網時,這個網關主機并沒有開啟snat機制,僅安裝了一個web服務的守護進程,但并不提供web服務,僅接收內網用戶訪問外網的請求;由于它工作在應用層,會拆封用戶的報文,從而根據里面的url、host等等,來判斷出用戶到底訪問的哪個主機,根據用戶請求的資源,判斷是否允許用戶訪問,可以在里面做拒絕訪問控制,例如訪問的是MP3而不是html網頁就拒絕;因為可清楚的知道訪問的是什么資源了;
如果允許內網用戶訪問,自己要當做客戶端,向后端真正提供服務的主機發起請求,web服務將響應給這個代理主機,代理主機再封裝響應報文給內網用戶;
通常一個web代理服務器,只代理80、443等相關協議;因此,第一,它的功能能力有限;第二,用戶要在客戶端程序上指明代理服務器的地址和端口;這種機制就叫正向代理;
為了提高用戶的性能,通常在代理服務器上設置緩存,用戶訪問時,先檢查本地是否有緩存條目,如果有直接用緩存的結構響應給客戶端,這樣性能會好很多,如果緩存放在固態硬盤或內存中效果還會更好;
緩存中的任何緩存項在緩存時是有有效期的,有效期限是在原始服務器上定義的,每一次在緩存時都會告訴客戶端這個資源可以緩存且能緩存多長時間;這樣用戶訪問頁面有可能訪問的結果都是來自于緩存的,尤其是靜態內容;
如果不想用緩存內容,就想訪問原始服務器的內容,則要在瀏覽器上使用shift+F5,強行刷新,表示在代理服務器發不要緩存內容就要原始服務器內容;
如果代理服務器聯系不上原始服務器,但是本地緩存已經過期,是否使用該過期的緩存項響應給客戶端,這取決于管理員的設置;
另外,緩存是有多級的,瀏覽器自己的緩存叫私有緩存,而代理服務器上的緩存叫公共緩存,很多數據私有緩存可緩存,但共有緩存不可緩存;例如,個人郵箱的內容;
反向代理
正向代理是幫助用戶訪問互聯網的任何服務器,是代表的用戶或代表的是請求者的身份訪問互聯網的任何服務器;
反向代理代表的是被請求者的身份,而且通常指代表被請求者的有限個被請求者;通常工作在某個服務器的前端,目的是自己工作在互聯網上著名端口上,只要有請求發來,自己代表了某網站的服務器,為了能夠保證后端服務器安全,不讓用戶直接訪問后端服務器;同dnat一樣,也是工作內核中的,能夠用于隱藏后端服務器,但是,不能檢查應用層,客戶端請求的是什么樣的資源,如果要做更精確的控制,使用應用層的代理服務器,在完成應用層分析之后向后端進行代理,才是更為安全的做法;
既然工作在應用層,像iptabes基于dnat也可以在forward鏈上添加filter規則實現過濾,代理服務器反向代理也可以;跟正向代理一樣甚至可以做到,根據客戶端請求的資源類型進行控制;根據請求文件的url后綴名就能判斷請求的是什么樣的資源,這在iptabes上是很難做到的;
反向代理也是工作在應用層的一個應用程序,這個程序文件應監聽在某個套接字上才能接收網絡上的客戶端的請求,這樣必須要基于某種并發響應模型,才能響應并發響用戶的請求;響應模型是什么取決于程序的并發響應機制;有可能是httpd的prefork或worker,也可能是nginx的master/worker模型響應的epoll機制;
不管使用什么機制,必須要基于套接字進行響應,就意味著:
- 第一,每一個連接都要維護一個套接字文件;
- 第二,由于代表了服務器在工作,每個用戶請求到來,如果本地沒有緩存,就要向后端服務器發請求,此時,扮演為客戶端的角色,既然是客戶端,同樣,每個客戶端也都需要打開一個套接字,然后連接服務器端;如果代理服務器收到并發1萬個請求,這1萬個請求,都要把自己扮成客戶端到后端服務器請求資源,也就愛意味著要打開1萬個打開,向后端服務器去取內容的;
實際在Linux可用的端口只有2萬個而已,這樣,代理服務器的并發響應能力受限于可用端口數;
如果要工作在內核級,比如lvs完全不需要端口,所以lvs能輕易突破端口數的限制;但是,這種反代服務器是突破不了的;對于前端正向代理只需一個80端口即可,而后端的反向代理則不能完成;
這就是反向代理服務器,自己首先是個進程,能分析用戶請求資源是什么,分析完成之后把自己扮演為客戶端向后端轉發;
所面臨的報文類型有兩種:
- 第一種類型,是自己作為服務器時收到的報文,源ip為CIP,目標ip為VIP;
- 第二種類型,向后端進行代理時是自己做為客戶端,此時收到的報文,源ip為RIP,目標ip為DIP;后端服務器是把報文響應給代理服務器的,代理服務器收到后,查看里面的內容封裝響應報文再響應給客戶端;
從這個角度,代理服務器比dnat性能要差很多,因為,它要拆服務器響應報文的所有封裝,還有再重新封裝起來;而對于dnat只需拆到ip首部即可,如果不做端口映射只把原ip目標ip改變就可,而應用層首部不會識別也不會改變里面的內容;反代則是必須把應用層首部拆封了再封裝,多了幾個步驟,所以從性能角度要差很多,姑且不論端口數量是否夠用,但帶來的好處是,由于它所探查的深度遠遠大于dnat功能,可以控制機制就要強大和靈活很多;于是,在并發要求不是特別高的場景中,反代服務器還是有這存在意義的;
為了給反代提速,也跟正向代理一樣,可以加緩存;把從后端服務器取得的內容緩存在本地,緩存時間受限于緩存策略,緩存中條目的管理也需要管理接口進行控制;
有緩存時,反代服務器就不用每次向后端服務器取資源,而是直接查本地緩存,如果有直接從緩存中取出結果響應給客戶端,從而給上游服務減輕了訪問壓力;
假如有1萬個請求,其中有7千個是命中在緩存中,只有3千個是從后端服務器讀取的,大大減小了服務器壓力;
所以,在這種場景中,可以做到前端主機是nginx,而后端主機是http,相比較http所提供的功能還是比nginx多,nginx特性在于輕量但特性就少很多;有些應用程序是依賴于httpd的特性的;但可以在httpd前面加個nginx反代,把從httpd取得的結果緩存下來,充分發揮nginx的優勢;
在應用場景中可做lnamp建構,動態內容發給httpd,httpd也是啟動也是啟動一個php模塊來php請求處理,靜態內容可以在nginx反代上完成;實現了動態內容、靜態內容分開的效果;而且反代不是fastcgi協議,而是http協議;
既然nginx能分析用戶請求的資源類型,nginx中的location可匹配用戶請求的url類型的后綴名;例如在代理時,寫一個location ~匹配.php結尾的都發給指定的服務器,是其它內容就發給另一服務器,這種就叫動靜分離;而這種分離的方式就是通過請求資源的類型后綴名來實現的;
對nginx來講做動靜分離,很輕易結合location+反代功能來實現;
靜態服務器可為一個組,動態服務器可為一個組,做成兩組不同的服務器,這樣達到的效果是:對于動態服務器才需要做會話保持,靜態服務器就可直接輪詢了;
兩組服務器還可做負載均衡,負載均衡時還可根據不同的調度算法進行,更重要的是不同內容可緩存性不一樣;很多動態內容不能緩存,但絕大多數都能緩存;因此,在靜態內容前端,添加一個專門的緩存服務器,而動態內容如果不需要緩存可不添加緩存服務器;
對于靜態內容,用戶期望無論被負載均衡的哪個節點上,所訪問的內容都應該是一樣的,但上傳內容時,需要動態實現的,因為動靜分開,所以要找一組分布式存儲;
上傳時通過動態服務器上的應用程序執行上傳操作,傳完以后url固定,用戶訪問時可通過靜態服務器加載后進行訪問,而且無論通過那個前端服務器加載,都是通過公共存儲進行加載的保證訪問的是同樣的數據;
nginx的反代,可以反代一個主機,也可負載均衡到多個主機上來,但是,這是兩種不同的機制;反代只需反代模塊就能工作,但反代到多個主機上這需要借助于另一模塊,稱為負載均衡模塊或叫upstream模塊來實現;
nginx在做高可用反代負載均衡時,用一個專門模塊需要將多個服務器在前端服務器定義成一個邏輯組,在組內定義了邏輯算法,而后反代時不是反代在主機上,而是反代在這個組上,而這個邏輯組就可理解為專門用于實現負載均衡的模塊,對nginx這個模塊就叫upstream模塊;
訪問網頁時會遇到502錯誤(bad gateway)原因
既然是代理服務器,整個過程是分成兩段的,第一段從客戶端到代理服務器,第二段從代理服務器到后端服務器,如果后端主機非常繁忙,在有限時間內,代理服務器沒能從后端服務器取得資源,客戶端就等待超時(例如3秒鐘),代理服務器在3秒鐘內沒取得資源就告訴客戶端,代理被拒絕;是這樣原因導致的502;
bad gateway的更多原因并不是因為讓客戶端少等時間,而是代理服務器等待后端服務器的超時時間太短導致;
ngx_http_proxy_module
用于反代用戶請求到后端主機,同時又支持緩存功能;工作方式同fastcgi使用fastcgi_pass指令一樣;
1、proxy_pass URL;
- 可實現動靜分離
- 能夠實現將請求發給后端URL指定的主機地址,這里之所以使用URL可以完成URL映射;例如前端的url為bbs,后端url可以是forum;
- 應用在location, if in location, limit_except的上下文;
例如:
proxy_pass http://localhost:8000/uri/;
這個uri可以不帶,如果后面沒有url時,proxy_pass會將location的url傳遞給后端主機;
proxy_pass后面的路徑不帶uri時,其會將location的uri傳遞給后端主機;
server {
...
server_name HOSTNAME;
location /uri/ {
proxy http://hos[:port];
}
...
}
前端訪問是:http://www.magedu.com/bbs --> 轉給真正訪問后端時:http://172.18.11.111/bbs
注意:HOST決不能帶/;
如果是proxy_pass http://HOST/;
則:前端訪問是:http://www.magedu.com/bbs --> http://172.18.11.111/
proxy_pass后面的路徑是一個uri時,其會將location的uri替換為proxy_pass后端主機的uri;
server {
...
server_name HOSTNAME;
location /uri/ {
proxy http://host/new_uri/;
}
...
}
前端訪問是:http://HOST/uri --> 替換為后端主機:http://HOST/new_uri/
如果location定義其uri時使用了正則式表達模式匹配機制,或在if語句或limt_execept中使用proxy_pass指令,則proxy_pass后的路徑必須不能使用uri;否則有語法錯誤;
server {
...
server_name HOSTNAME;
location ~|~* /uri/ {
proxy http://host;
}
...
}
http://HOSTNAME/uri/ --> http://host/uri/;
示例
后端RS1服務器內網:192.168.1.3
前端A服務器:
外網:172.18.11.111
內網:192.168.1.2
RS1啟動httpd并提供頁面:
[root@rs1 ~]# systemctl start httpd.serivce
[root@rs1 ~]# vim /var/www/html/index.html
<h1>RS1-192.168.1.3</h1>
在A主機安裝nginx
[root@hosta ~]# rpm -ivh nginx-1.8.0-1.el7.ngx.x86_64.rpm
[root@hosta ~]# rpm -ql nginx
[root@hosta ~]# vim /etc/nginx/nginx.conf
其中:
include /etc/nginx/conf.d/*.conf;
表示每個*.conf定義了一個服務器;
只需在/etc/nginx/conf.d/default.conf
配置即可:
[root@hosta ~]# cp /etc/nginx/conf.d/default.conf{,.bak}
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
只需添加代理:
location / {
#root /usr/share/nginx/html;
proxy_pass http://192.168.1.3;
index index.html index.htm;
}
在瀏覽器訪問:http://172.18.11.111
顯示:RS1-192.168.1.3
表明已經把請求代理到后端的RS1上了;
在RS1上創建一bbs的頁面:
[root@rs1 ~]# mkdir /var/www/html/bbs
[root@rs1 ~]# vim /var/www/html/bbs/index.html
<h1>BBS</h1>
在瀏覽器訪問:http://172.18.11.111/bbs/
顯示:BBS
表示直接映射到后端;其實,也可以只映射某一個url;
編輯location有url:
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /bbs/ {
proxy_pass http://192.168.1.3;
}
在瀏覽器訪問:http://172.18.11.111
顯示:nginx提供的歡迎頁;
如果在瀏覽器訪問:http://172.18.11.111/bbs
顯示:BBS
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
location /bbs/ {
proxy_pass http://192.168.1.3/;
}
如果在瀏覽器訪問:http://172.18.11.111/bbs
顯示:RS1-172.18.11.8
完成路徑映射;
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
location ~* \.(jpg|gif|png)$ {
proxy_pass http://192.168.1.3;
}
把jpg等結尾的資源都傳到后端;
如果在瀏覽器訪問:http://172.18.11.111/1.jpg
顯示:圖片在RS1的/var/www/html/1.jpg
如果定義php結尾的動態資源傳給后端主機,其它資源發往另一主機,如果能匹配動靜的資源就可實現動靜分離;
演示動靜分離:
開啟一臺RS2主機,可以只接把它配置成php-fpm,前端nginx代理時,就可使用fastcgi;
也可配置成http+php,前端nginx可通過基于http向后端代理;
以http+php,前端nginx可通過基于http向后端代理為模型演示;
RS2:192.168.1.4
安裝http+php
[root@rs2 ~]# yum -y install php httpd
...
并提供php測試頁:
[root@rs2]# vim /var/www/html/index.php
<h1>RS2-172.18.11.9</h1>
<?php
phpinfo();
?>
在前端DR1代理上配置:
只把php發給RS2
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
location / {
# root /usr/share/nginx/html;
proxy_pass http://192.168.1.3;
index index.html index.htm;
}
location ~* \.php$ {
proxy_pass http://192.168.1.4;
}
開啟RS1和RS2的httpd服務
[root@rs1 ~]# systemctl start httpd.service
[root@rs2 ~]# systemctl start httpd.service
在瀏覽器訪問:http://172.18.11.111/
顯示:RS1-172.18.11.8
在瀏覽器訪問:http://172.18.11.111/1.jpg
顯示:顯示圖片
在瀏覽器訪問:http://172.18.11.111/index.php
顯示:RS2-172.18.11.9和php的測試頁;
以上實現了nginx當做代理服務器,把http的靜態資源和php的動態資源分離轉發給后端不同的主機;
注:在做實際實驗時,把php安裝一個phpmyadmin有可能無法完成顯示正常,因為有些靜態頁面是在動態頁面基礎上生成的;需要在前端進行url重寫;
2、proxy_set_header field value;
設定向后端主機發送的請求報文的首部及其值;或是在原有首部后添加新值;
用在http, server, location上下文;
能抓取終端用戶IP地址,field可自定義;默認為$proxy_host代理主機的IP地址;可以設定任何首部的值;
示例:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwared_for;
server {
listen 80;
server_name www.ilinux.io;
location /blog/blog.html {
#在代理服務器上將遠程客戶端的Ip設置為X-Real-IP
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://10.10.10.11/news/news.html;
}
}
#在后端服務器,設置access日志的格式,將原本的$remote_addr替換為$http_x_real_ip
log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#在測試訪問幾次后,查看后端服務器的access_log文件即可查看到相應的訪問客戶端IP
表示如果X-Forwarded-For有首部值,會把$remote_addr的值補充在后面;為什么要補充,因為真正的代理費服務器是可以多級代理的;一個客戶端可以有多級正向代理,反向代理也可有多級,這樣在后端服務器日志記錄的有可能不是最終用戶的iP地址;所以像這種多級代理場景,每一個代理服務器都會附加一個新首部值,最終會知道最左側的值是終端用戶的iP地址;
在http的核心模塊中引入了很多內建變量:
其中,$remote_addr代表客戶端的IP地址;可把這個地址設置成請求報文的首部,nginx代理可把這個首部傳遞給后端服務器;從而實現后端服務器從中記錄終端用戶的IP地址到自己的日志記錄中;
編輯在一個server中生效:
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
server {
...
proxy_set_header X-real-ip $remote_addr;
...
}
還要在RS1中更改,日記記錄的格式:
[root@rs1 ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%{X-real-ip}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
在瀏覽器輸入:http://172.18.11.111/
在RS1上查看httpd的日志信息:
[root@rs1 ~]# tail /var/log/httpd/access_log
可見原地址為win7系統物理主機的ip地址:172.18.11.1;
跟nginx緩存相關的選項(緩存要先定義,后調用)
3、proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size];
只能用于http上下文;
可選項:[loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time]
選項參數 | |
---|---|
keys_zone=name:size | 指明內存空間名稱和大小 |
path | 磁盤上緩存的文件系統路徑 |
levels=levels | 緩存目錄層級,最多有3級,每級最多2個字符(目錄) |
use_temp_path=on|off | 是否啟用臨時文件路徑 |
inactive=time | 緩存中緩存項(k/v數據)的非活動時間;默認10分鐘 |
max_size=size | 緩存文件系統路徑的大小;一旦存滿了基于LRU(最近最少使用)算法做清理 |
示例:proxy_cache_path /usr/local/nginx/proxy_cache_dir/cache1 levels=1:2 keys_zone=cache1:100m inactive=1d max_size=10g;
4、proxy_cache zone | off;
指明要調用的緩存,或關閉緩存機制;
配置段:http, server, location
示例:
server {
...
server_name HOSTNAME;
location /uri/ {
proxy http://hos[:port];
proxy_cache cache1;
}
...
}
5、proxy_cache_key string;
定義緩存關鍵字;
示例:
proxy_cache_key $request_uri
proxy_cache_key $scheme$proxy_host$request_uri
多個變量的值作為一個字符串使用,這樣,即便訪問的是同一url,如果使用的協議不同,對應的緩存項也不一樣;因此,key就決定了兩個緩存項是否為同一個;
6、proxy_cache_valid [code ...] time;
為不同的響應碼設定其緩存的時長;
示例:
proxy_cache_valid 200 303 10m;
proxy_cache_valid 404 1m;
響應碼沒定義的表示不緩存;
定義緩存:
[root@hosta ~]# vim /etc/nginx/nginx.conf
在http中定義:
proxy_cache_path /var/cache/nginx/myproxy levels=2:1:1 keys_zone=mycache:10m max_size=1g;
表示:
/var/cache/nginx/myproxy:定義緩存保存路徑,如果目錄不存在,要事先創建;
levels=2:1:1 創建3級緩存目錄,每一級用1個字符;
max_size=1g:緩存的文件可用空間大小為1G;
keys_zone=mycache:10m:緩存名為pcache,大小為10M;
在server中定義調用緩存的名稱:
[root@hosta ~]# vim /etc/nginx/conf.d/default.conf
location / {
# root /usr/share/nginx/html;
proxy_cache mycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://192.168.1.3;
index index.html index.htm;
}
其中:
proxy_cache pcache 調用緩存名稱;
proxy_cache_key $request_uri 指定緩存中的key為請求的uri;
proxy_cache_valid 200 302 10m 緩存響應碼為200,302的資源10分鐘;
proxy_cache_valid 404 1m 緩存響應碼為404的資源為1分鐘;
在瀏覽器測試:http://172.18.11.111/
多次刷新頁面,以便生成緩存;
查看前端nginx代理中的是否有緩存生成:
[root@hosta ~]# ls /var/cache/nginx/myroxy
可查看到有2個目錄,每個目錄下只一個目錄,到第三級目錄即是文件;
可使用tree命令查看目錄層級結構:
[root@hosta]# tree /var/cache/nginx/myproxy/
/var/cache/nginx/myproxy/
├── 01
│ └── 9
│ └── c
│ └── 8dfbf1f445e33bc957e156659229c901
└── d9
├── 7
│ └── c
│ └── 6666cd76f96956469e7be39d750cc7d9
└── a
└── 8
└── e0bd86606797639426a92306b1b98ad9
7、proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ...;
指明在什么情況下是否使用過期緩存;
當代理服務器后后端主機通信發生錯誤時,在哪種錯誤情況下可以使用緩存中的內容,直接響應給客戶端;
代理可使用的選項:
proxy_cache_min_uses 最少使用次數;
proxy_cache_methods 為哪些請求方法緩存,默認只為get緩存;
proxy_cache_bypass 繞過緩存;
proxy_cache_purge 對緩存進行修剪;
在前端代理服務器設置緩存可緩存2天,但后端服務器剛緩存半天,緩存就變化了;客戶端請求時發現,都是從緩存中響應的,而原始數據實際已經改過了,作為管理員,只能手動連接到緩存服務器上把緩存項清除;這個操作就叫緩存修剪;
8、proxy_cache_methods GET | HEAD | POST ...;
配置段:http, server, location
當客戶端使用指定的請求方法發送請求時,該請求會被緩存下來。GET和HEAD是默認就被指定的緩存方法。
9、proxy_hide_header field;
配置段:http, server, location
默認情況下,nginx不會將報文首部“Date”,“Server”等字段從代理服務器傳遞給客戶端。而此指令,可讓管理員設置額外的不進行傳遞的字段。
10、proxy_connect_timeout time;
與后端服務器建立連接的超時時長,默認為60秒,最長為75秒;
11、proxy_read_timeout time;
等待后端主機發送響應報文的超時時長,默認為60秒;此處定義的時長指的是連續兩次響應報文等待之間的時長;很有可能需要調整;兩個等待后端響應報文的時長;
12、proxy_send_timeout time;
向后端服務器發送請求報文的超時時長,默認為60秒,指的是連續兩次請求報文(寫操作)之間的時長,不是整個請求本身;兩個請求報文發送后端的時長;
ngx_http_headers_module
用于在響應給客戶端的報文中添加首部值
1、add_header name value [always];
向響應給客戶端的報文添加自定義首部,并賦值;
可用于http, server, location, if in location上下文;
例如:$server_name或$server_addr
$server_addr
表示接收請求報文服務的地址即代理服務器地址;
server {
listen 80;
server_name www.ilinux.io;
location /blog/blog.html {
proxy_set_header X-Real-IP $remote_addr;
#添加自定義首部X-via表示代理服務器IP
add_header X-Via $server_addr;
#添加自定義首部X-Accel表示代理服務器的主機名
add_header X-Accel $server_name;
proxy_pass http://10.10.10.11/news/news.html;
}
}
2、expires [modified] time;
expires epoch | max | off;
控制與緩存相關的首部;
用于添加Expire及Cache-Control首部或修改首部的值;后面講到緩存服務時具體講緩存細節,這里不做過多介紹;
當響應碼為200, 201, 204, 206, 301, 302, 303, 304, or 307添加一個首部,或修改一個值;就是控制如何緩存,包括緩存時間
#表示指定路徑上的圖片在客戶端上緩存3天才失效
location ~* \.(gif|jpg|jpeg|png) {
root /var/mywww/html/public/
expires 3d;
}
ngx_http_fastcgi_module模塊
LAMP(fpm):
httpd+php:3種
modules:把php編譯成httpd自己的模塊;
cgi
fastcgi
httpd依賴于proxy_fastcgi_module模塊
LNMP():
nginx+php(fpm):只有1種
nginx編譯時只支持fastcgi模塊,因此php也只能使用php-fpm機制;fpm就是fastcig processor manager進程管理器;
當有大量數據進行保存時,要放在專用的存儲中,需要專用的協議客戶端,php程序代碼,要與后端mysql通信,就要php鏈接mysql的驅動,叫php鏈接器;不同程序員使用不同的鏈接器,存儲mysql數據庫;
當用戶請求有動態資源時,就交由后臺的php服務器進行處理,如果還要有數據存儲,就由php再與myslq通信,從而,將結果返回給客戶端;
后端存儲會存在多個客戶端與數據庫服務器通信,此時會遇到資源征用,可能會導致資源響應較慢;
以后,優化時,可根據應用來判斷,問題的所在;
1、fastcgi_pass address;
指明后端php-fpm服務器的address;是fpm服務器監聽的地址和端口;
示例:fastcgi 127.0.0.1:9000;
2、fastcgi_index name;
配置段:http, server, location
設置fastcgi的默認主頁資源,如果URI以斜線結尾,文件名將追加到URI后面,這個值將存儲在變量$fastcgi_script_name中。
例如:
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;
請求uri/page.php
的參數SCRIPT_FILENAME將被設置為/home/www/scripts/php/page.php
,但是"/"為"/home/www/scripts/php/index.php"。
3、fastcgi_param parameter value [if_not_empty];
配置段:http, server, location
此指令用于指定要傳遞給FastCGI服務器的參數,參數可以為文本、變量和兩者之間的組合。
示例1:
#將匹配的php內容送到fastcgi服務器的/usr/share/nginx/html目錄下進行處理,即指定fastcgi服務器上用于存放PHP內容的目錄
location ~* \.php$ {
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
include fastcgi_params; #調用nginx的變量定義;
}
示例2:
#將狀態頁面status和檢測頁面ping送到fastcgi進行處理
location ~* ^/(status|ping)$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
4、fastcgi_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
定義緩存空間的名稱;只能用于http上下文;
參數 | |
---|---|
path | 文件系統路徑,用于存放緩存的文件數據;在此目錄下分多級目錄 |
max_size=size | 定義此路徑下多大空間用于存儲緩存數據 |
其它使用默認設置即可 | |
levels=#[:#[:#]] | 緩存目錄的層級定義;一般為1或2;如:leves=1:2:2 |
keys_zone=name:size | 定義內存中用于緩存k/v映射關系的空間名稱及大小 |
inactive=time | 定義非活動時間 |
示例:fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
nginx緩存實現加速,php執行代碼的結果保存在nginx的緩存空間中,用戶再請求時就不會交給后端的php在執行了,而是直接從緩存返回給客戶端,這就相當于靜態資源了;httpd基于模塊也能緩存;
緩存結果內容,有可能剛存下來,資源被刪了,客戶端請求時發現資源仍然可以響應,這樣就不真實了,所以,緩存要及時清理;
緩存下來的內容,得有命中率,要有客戶端經常訪問,否則沒有意義;同一資源被多次請求的經歷;
nginx把緩存分為兩段:
首先在內存空間中保存緩存文件的元數據;然后在磁盤中保存元數據所指向的文件內容;
所以,對于nginx緩存保存的是鍵值數據,把文件系統上的層級文件數據,轉換成Key-Value數據,key在內存中,value在磁盤中是一個個的文件對象;而文件的名字等信息是在內存中的;
key也是分層級的但不同于文件系統的層級;k是文件的名字或是訪問的URL,而v是真正的數據;數據文件的名字是這個文件內容的校驗碼;
緩存下來的文件的文件名是文件內容做md5校驗計算后的校驗碼,md5校驗碼是128位二進制定長輸出;每四位二進制對應一個十六位進制數,以md5為例,128/4=32,即32個十六進制數;文件內容不同校驗碼就不同;把這些十六進制數字組成的文件名進行層級劃分;
可把前兩個字符當作一級子目錄的名字,再兩字符當二級子目錄的名字,再一個字符當三級子目錄的名字;這樣意味著,一級子目錄有(兩個十六進制數字表示的個數)即256個,二級子目錄有256個。三級子目錄有16個;
想要分幾級,就用多少個十六進制數所能劃分的個數來表示;
5、fastcgi_cache zone|off;
是否啟用cache功能,如果啟用,要提前定義緩存空間的名字;默認off不緩存;
6、fastcgi_cache_key string;
定義要使用的緩存鍵;
例如:fastcgi_cache_key $request_uri
7、fastcgi_cache_methods GET | HEAD | POST ...;
緩存哪些類型的請求的相關數據;是請求方法;
8、fastcgi_cache_min_uses number;
緩存最少使用次數;在指定時長內,如果小于此值就為非活動;
9、fastcgi_cache_valid [code ...] time;
緩存數據時,對不同響應碼設定其可緩存的時長;
注意:調用緩存時,至少應該制定3個參數
fastcgi_cache
fastcgi_cache_key
fastcgi_cache_valid
例如:
啟用緩存前,使用ab命令壓測
[root@VM_0_2_centos ~]# ab -c 10 -n 100 https://172.18.11.114/info.php
平均響應請求為300個左右;
開啟nginx緩存功能:
[root@VM_0_2_centos ~]# vim /etc/nginx/nginx.conf
在http配置段:
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=fcgicache:10m;
調用緩存:
location ~ \.php$ {
root html;
fastcgi_cache fcgicache; #調用fache緩存空間
fastcgi_cache_valid 200 302 10m; #狀態碼為200和302的頁面緩存10分鐘
fastcgi_cache_valid 301 1h; #狀態碼為301的頁面緩存1小時
fastcgi_cache_valid any 1m; #剩下的都緩存1分鐘
fastcgi_cache_key $request_uri; #設置緩存的key為$request_uri
fastcgi_pass 127.0.0.1:9000; #指定fastcgi服務器
fastcgi_index index.php; #指定默認的fastcgi主頁
fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html/$fastcgi_script_name;
include fastcgi_params; #調用nginx的變量定義
}
[root@VM_0_2_centos ~]# nginx -t
報錯,創建目錄;
[root@VM_0_2_centos ~]# mkdir /var/cache/nginx/fastcgi -pv
[root@VM_0_2_centos ~]# nginx -s reload
在瀏覽器:https://www1.stu11.com/info.php
多次刷新頁面后,查看生成的緩存文件:
[root@VM_0_2_centos ~]# ls /var/cache/nginx/fastcgi/
5 7 b
[root@VM_0_2_centos ~]# tree /var/cache/nginx/fastcgi/
/var/cache/nginx/fastcgi/
├── 5
│ └── 34
│ └── 751b46a83f276bdacf136a8e2ee9f345
├── 7
│ └── 3a
│ └── e57acb73a5225739581dfc2a8f48b3a7
└── b
└── 23
└── 41b24196f66c4d900cc5c11aa2d3a23b
[root@VM_0_2_centos ~]# ab -c 10 -n 100 https://172.18.11.114/info.php
有顯著提升,平均響應為400左右;
ngx_http_upstream_conf_module模塊
將后端主機定義為服務器組(用來定義服務器組),而后可有proxy_pass,fastcgi_pass,memcached_pass等進行引用;
是動態配置upstream服務器的一個接口;
Module ngx_http_upstream_module
將多個后端主機定義為服務器組,而后可由proxy_pass,fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass等進行引用;
用于定義服務器組,用一組服務器可由代理服務器向外發布服務接收、請求;這組服務器可基于某種方式定義調度算法(如ip_hash,least_conn),來實現所謂的負載均衡調度;
此模塊才是真正能把nginx當做反向代理負載均衡器的模塊,定義成組不光能被proxy_pass使用,還可被fastcgi_pass, uwsgi_pass, scgi_pass, and memcached_pass使用;
1、upstream name { ... }
定義后端服務器組;只需指定組名,引入新的上下文,上下文就叫upstream上下文;
只能用于http上下文;
name:名稱,直接字符串;
示例:
upstream httpdsrvs {
server 10.10.10.11:80;
server 10.10.10.12:80;
...
}
2、server address [parameters];
添加服務器,定義服務器的地址和相關參數;默認為80端口;
address地址格式 | |
---|---|
IP[:port] | 指定后端主機IP地址和端口 |
HOSTNAME[:port] | 指定后端主機的某虛擬主機,要在前端代理的hosts文件中定義代理的虛擬主機名對應的ip地址是什么,避免dns掛了不能被訪問 |
unix://PATH/TO/SOME_SOCK_FILE | 開頭是unix的路徑 |
parameters參數 | |
---|---|
weight=number | 服務器主機的權重值;在最少連接和輪詢調度時,默認的意義是加權輪詢或加權最少連接; |
max_fails=number | 最大失敗嘗試次數(與后端服務器連接最大不成功嘗試次數) |
fail_timeout=time | 設置服務器被識別為不可用的超時時長 |
backup | 備用主機,相當于定義為sorry server;所有后端主機都不可用時才生效 |
down | 手動標記為下線(不再處理任何用戶請求),維護時使用 |
后端如果是商城程序的應用程序服務器時,當要進行新版本程序發布,設置要應用此設置;在摘除前確保本服務器沒有會話保持,可用session服務器做會話保持,這樣即便摘除,客戶訪問時再次刷新會重新調度到在線服務器上;就在前端代理服務器對應的server上添加down,標記為下線,就可摘除后端服務器進行升級等操作,完成后再接入系統中,把down標記刪除即可;觀察一段時間后沒有問題,就可以此方法,對其它服務器進行升級操作,這種機制就稱灰度發布模型;數量多時可一批一批進行,有能力會寫腳本自動完成;但是腳本完成比較麻煩,可使用python程序完成;
一般先標記為down,然后重載nginx,就不會有新的請求進來,但是老的請求還在,因為是平滑摘除的;一個請求對服務器最長不能超過5秒鐘;5秒鐘以后,就可把后端服務停掉了;
例如:nginx實現為后端2主機做反代
后端服務器:
RS1:192.168.1.3
RS2:192.168.1.4
前端nginx反代:
VIP:172.18.11.111
DIP:192.168.1.2
后端服務器:
創建網頁提供http服務
[root@rs1 ~]# vim /var/www/html/index.html
<h1>RS1-172.18.11.8</h1>
[root@rs2 ~]# vim /var/www/html/index.html
<h1>RS2-172.18.11.9</h1>
在nginx反代服務器上基于負載均衡,要編輯http的上下文;
[root@niginx]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=1;
server 192.168.1.4:80 weight=1;
}
...
}
在指定的server中調用;
[root@niginx ~]# vim /etc/nginx/conf.d/default.conf
server {
...
location / {
# root /usr/share/nginx/html;
#add_header X-Via $server_addr;
# proxy_cache mycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://websrvs; 注意此處要改為主配置文件中定義的服務器組名稱;
index index.html index.htm;
}
...
}
在瀏覽器輸入:http://172.18.11.111/
一次次刷新,可發現響應的結果是RS1和RS2中的頁面,交替提供響應;
也可把權重改下,如:server 192.168.1.3:80 weight=2;
則在瀏覽器輸入:http://172.18.11.111/
一次次刷新,可發現,響應結果為,RS1響應2次后轉為RS2響應1次,按照2:1的比率,依次交替響應;
3、ip_hash;
原地址哈希調度算法;
只能用于upstream上下文;
能夠使來自于同一個客戶端請求始終到同一個RS;
哈希的是客戶端ip,相當于lvs中的sh算法,哈希表中的key是客戶端ip,位于同一個nat服務器后面的所有客戶端(內網用戶),會始終被調度到一個服務器響應,調度粒度過于粗糙;
例如:
[root@niginx ~]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=2;
server 192.168.1.4:80 weight=1;
ip_hash;
}
...
}
使用curl命令測試
[root@niginx]# curl http://172.18.11.111
發現,使用同一終端ip,始終由一個服務器響應;實現了原地址哈希,綁定了客戶端與服務器;
4、least_conn;
如果weight有值且不相同,就相當于加權最少連接調度算法;
只能用于upstream上下文;
考慮后端服務器當前負載進行調度;計算方法跟lvs中很相似,活動數連接/權重;有可能結果不是很對稱,這跟超時時間有關;
例如:
[root@niginx ~]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=2;
server 192.168.1.4:80 weight=1;
least_conn;
}
...
}
使用curl命令測試
[root@niginx ~]# curl http://172.18.11.111
發現,由兩臺RS交替響應,比例為2:1
5、keepalive connections;
設置保持連接個數;可節約套接字和端口;激活后端服務器組緩存,主要設置保持連接時長(實際是連接個數);超出連接的個數時,最近最少使用的連接LRU被關閉;
通常,在前端反代服務器與后端服務器間打開保持連接功能,可節約前端反代服務器的端口(套接字);
upstream http_backend {
server 127.0.0.1:8080;
keepalive 16;
}
#對于fastcgi服務器來說,keepalive要結合fastcgi_keep_conn指令一起使用才能生效
upstream fastcgi_backend {
server 127.0.0.1:9000;
keepalive 8;
}
server {
...
location /fastcgi/ {
fastcgi_pass fastcgi_backend;
fastcgi_keep_conn on;
...
}
}
6、health_check [parameters];
定義后端主機的健康狀態檢測機制;只能用于location上下文;
如果不定義此處,默認在定義server時也會有最大失敗嘗試次數,失敗的超時時長等,已經有一定意義的健康狀態檢測機制;
要使用match判斷匹配健康機制;
parameters可用參數 | |
---|---|
interval=time | 檢測的頻度,默認為5秒 |
fails=number | 判定為失敗的檢測次數;判斷服務器不可用的檢測次數;默認1次,盡量使用3次; |
passes=number | 判定為成功的檢測次數;判斷服務器為可用的檢測次數;默認1次; |
uri=uri | 執行健康狀態檢測時試圖請求的uri;不指明默認為主頁/;表明在請求這個url時,其響應結果必須是在match中匹配的 |
match=name | 基于哪個match做檢測結果為成功或失敗的判定;用誰進行評估檢測結果;指明調用哪個macth(做健康檢測判斷)判斷成功或失敗; |
port=number | 指明向服務器的哪個端口發起健康狀態檢測請求 |
服務器可監聽在2個端口,例如用8080端口用來接收健康狀態檢測,80端口向外提供服務;這樣可以把8080端口做一個虛擬主機,而后對這個內容不記錄在訪問日志中;所以作為健康狀態檢測,有可能對于后端主機要關閉日志功能;
如果不定義此參數,后端主機也會自動由在線主機負責所有響應;
可把RS2主機關閉http服務,在瀏覽器上測試,依然能訪問主頁;
再把RS2主機啟動http服務,再進入瀏覽器測試,會發現恢復到原來依次交替負責響應;
7、match name{...}
只用于http上下文,對后端主機做健康狀態檢測時,定義其結果判斷標準;
結果 | |
---|---|
status 200; | 響應碼為200時認為是成功 |
status ! 500; | 響應碼只要不是500就認為是成功 |
status 200 204; | 響應碼為200或204認為是成功 |
status ! 301 302; | 響應碼只要不是301,302就認為是成功 |
status 200-399; | 響應碼為200至399之間的值認為是成功 |
status ! 400-599; | 響應碼為不是400至999之間的值認為是成功 |
status 301-303 307; | 響應碼為301至303之間或307的值認為是成功 |
header Content-Type = text/html; | 頭部是text/html認為是成功 |
header Content-Type != text/html; | 頭部不是text/html認為是成功 |
header Connection ~ close; | 頭部能被close匹配認為是成功 |
header Connection !~ close; | 頭部不能被close匹配認為是成功 |
header Host; | 頭部存在host頭部認為是成功 |
header ! X-Accel-Redirect; | 頭部不存在此頭部認為是成功 |
body ~ "Welcome to nginx!"; | 被此處匹配的body認為是成功 |
body !~ "Welcome to nginx!"; | 不能被此處匹配的body認為是成功 |
專用指令:
status:期望的響應碼:
status CODE
status ! CODE
status CODE-CODE
header:基于響應首部進行判斷
header HEADER=VALUE
header HEADER!=VALUE
header [!]HEADER
header HEADER ~ VALUE
body:期望的響應報文的主體部分應該有的內容
body ~ "CONTENT"
body !~ "CONTENT"
例如:
[root@nginx ~]# vim /etc/nginx/nginx.conf
http {
...
match health {
status 200;
body ~ "OK";
}
...
}
[root@nginx ~]# vim ./conf.d/default.conf
在server中調用
server{
...
location / {
# root /usr/share/nginx/html;
#add_header X-Via $server_addr;
# proxy_cache mycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://websrvs;
index index.html index.htm;
health_check match=health interval=2 fails=3 uri=/.health.html;
}
...
}
注意:nginx2.0+以上版本才支持health_check相關功能;
8、sticky cookie name [expires=time] [domain=domain] [httponly] [secure] [path=path];
啟用session綁定,基于cookie(會話)的綁定機制;
只用于upstream上下文;
比ip_hash更為精細的精細會話保持綁定;
參數選項:
cookie name 定義server id
expires=time 定義cookie有效時長,即會話綁定時長;
domain=domain 定義cookie應用在哪個域上;
httponly 定義cookie僅用在http協議上;
secure 給cookie添加安全首部
path=path 指定某個域中的某個路徑下哪些內容做cookie綁定
例如:
sticky cookie srv_id expires=1h domain=.example.com path=/;
srv_id 把哪個server id當做cookie的內容插入的用戶的作業中;
expires=1h 指明有效時長;
domain=.example.com path=/ 應用在哪個域上的哪個路徑上;
例如:
[root@nginx ~]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=2;
server 192.168.1.4:80 weight=1;
sticky cookie srv_id expires=1h path=/;
}
...
}
注意:在nginx1.8版本不支持此功能;
9、hash key [consistent];
定義調度方法,可自定義基于何種信息(key)進行綁定;如nginx可基于cookie、用戶請求的host首部、用戶請求的uri綁定;
例如:
hash $remote_addr
基于請求的客戶端地址進行綁定;相當于ip_hash;
hash $request_uri
客戶端請求的url
hash $cookie_username
來自同一用戶賬號進行綁定
例如:基于客戶端源IP的綁定
[root@niginx ~]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=2;
server 192.168.1.4:80 weight=1;
hash $remote_addr;
}
...
}
在瀏覽器輸入:http://172.18.11.111
多次刷新,仍然為同一RS響應;實現基于客戶端源IP的綁定;
在各RS上創建多個網頁,測試基于url的綁定:
[root@rs1 ~]# for i in {1..10};do echo "$i page on RS1" > /var/www/html/$i.html;done
[root@rs2 ~]# for i in {1..10};do echo "$i page on RS2" > /var/www/html/$i.html;done
例如:基于用戶請求的url的綁定
[root@niginx ~]# vim /etc/nginx/nginx.conf
http {
...
upstream websrvs {
server 192.168.1.3:80 weight=2;
server 192.168.1.4:80 weight=1;
hash $request_uri;
}
...
}
在瀏覽器輸入不同的資源:
http://172.18.11.111/1.html
http://172.18.11.111/2.html
http://172.18.11.111/3.html
可觀察,由于使用的2:1的權重,在uri進行綁定時也可看出效果來;
注意:基于url綁定時的好處在于,后端如果是緩存服務器時提高緩存命中率;
其中內置變量:
$upstream_addr upstream
服務器自己的地址
$upstream_cache_status
緩存命中與否,引用后可知道是從緩存中響應的,還是從后端服務器響應的;
注意:只要能進行哈希的值都可作為綁定對象;
補充:內置變量的調用,向客戶端展示緩存命中與否;
add_header X-Cache $upstream_cache_status;
nginx也可實現更高級的基于方法的分離,get,put叫讀寫分離等;
例如:開啟緩存,查看請求是由緩存響應還是由后端服務器響應,引入$upstream_cache_status
[root@niginx ~]# vim /etc/nginx/nginx.conf
server {
...
location / {
# root /usr/share/nginx/html;
add_header X-Via $server_addr;
add_header X-Cache $upstream_cache_status;
proxy_cache mycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://websrvs;
index index.html index.htm;
# health_check match=health interval=2 fails=3 uri=/.health.html;
}
...
}
在瀏覽器F12打開調試模式:
輸入:http://172.18.11.111/1.html
點擊第一次,可見為:X-Cache:MISS 表示由后端服務器響應的;
點擊第二次,可見為:X-Cache:HIT 表示由緩存響應的;