Nginx 代理與緩存
Nginx可用于反向代理,客戶端向代理服務器發出請求,代理服務器封裝重新請求報文并向后端服務器發出請求,再由代理服務器響應客戶端,并且在一定條件下能夠緩存URL,以減小Upstream Server的壓力。同時,反向代理服務器也實現了隱藏后端服務器的效果。
Nginx作為代理服務器時工作在應用層與傳輸層,能夠對http請求報文進行分類、修改、轉發、以及調度Upstream Server或服務器組,以實現負載均衡之目的。Nginx通過理解報文Header中的Method
,URL
,Version
,Host
,Connection
等信息以實現報文的分類,并封裝成一個新的請求報文,如在應用層修改請求文本Connection為Keepalive
,如在傳輸層修改目標端口為8080
等。新的報文由代理服務器向后端服務器發出請求。為了解決后端服務器記錄的是真實的客戶端IP地址,Nginx會增加一個X-Forward-For
項。
Nginx作為代理服務器具有緩存功能,客戶端首次請求時那些被允許緩存的URL將以key/value的hash值存放在層級目錄結構中,客戶端再次請求同一URL時,如果請求報文的Header中Method是GET或HEAD時,將促使代理服務器查詢緩存,并且直接響應給客戶端,極大地釋放后端服務器的壓力。當緩存空間滿時或緩存過期時將使用LRU算法進行清理,為了避免清理的緩存依然是有效的,響應報文中的status為304,代理服務器將為其續期。
狀態碼:304,not modify
如果客戶端發送的是一個條件驗證(Conditional Validation)請求,則web服務器可能會返回HTTP/304響應,這就表明了客戶端中所請求資源的緩存仍然是有效的,也就是說該資源從上次緩存到現在并沒有被修改過。條件請求可以在確保客戶端的資源是最新的同時避免因每次都請求完整資源給服務器帶來的性能問題。
【問題一】:URL是否允許被緩存,緩存多長時間,以及壓縮資源如何緩存?
由后端服務器告知代理服務器,如果包用戶含敏感信息則不允許緩存;響應報文的Header中包含了緩存控制信息,包括緩存是否被允許、緩存有效期等,Vary中包含了壓縮信息。
【問題二】:緩存命中率不高的問題?
緩存不被命中時將導致代理服務器不僅要查詢緩存,還要再次向后端服務器請求,造成額外的開銷。
【問題三】:緩存過期時是否清理?
代理服務器根據緩存有效期清理緩存時需要考慮后端服務器相應的資源是否發生改變,如果未發生改變則不必要清理緩存。通常Nginx清理緩存時會詢問后端服務器是否可以清理緩存,如果響應報文中的status為304則表示緩存依然有效,此時不必清理該URL的緩存。
【問題四】:后端服務器如何記錄客戶端IP?
代理服務器作為后端服務器的客戶端,導致后端服務器無法記錄真實客戶端的IP,因此Nginx通過增加一個自己的Header,并增加客戶端IP地址到X-Forward-For中,因此后端服務器能夠記錄真實客戶端IP。多級代理服務器發現有X-Forward-For首部則不修改一級代理封裝的報文,直接轉發給后端服務器。
【問題五】:代理服務器如何把同一URL的請求始終分發至同一臺服務器?
方法一是對URL做hash再取模(服務器數量)得出非常固定的服務器調度方式,但是某一服務器故障時會取模hash算法失效,無法實現URL與主機的對應。
方法二是對服務器ip地址和URL一致性hash,并取模于32,相當于每臺服務器分布在一個(2^32)哈希環上,按照轉圈的方法把URL的hash值定位到某一服務器上。如果圈上就近的服務器故障,則向下一臺服務器請求。
【問題六】:一致性哈希算法導致部分服務器負載不均衡的問題
由于hash環有2^32位取值,如果服務器之圈定的范圍較大,就會造成該服務器偏斜,為了實現均衡,可對每臺服務器做多次不同salt
的hash計算,以虛擬服務器的方式分布在hash環上。
【問題七】:CDN之間代理的問題
為了提高各地區的響應速度,通常會購買多個CDN服務。CDN則向LVS集群發出請求,LVS負載均衡Nginx代理服務器集群,代理服務器在應用層封裝新的請求報文至Varnish緩存服務器集群,如果緩存服務器未命中URL資源,則最終向后端Web服務器集群請求相應的資源。這是根據客戶端請求的資源類型、客戶端的位置、客戶端使用的網絡提供商等信息構建的網絡拓撲,目的就是增強Web服務的性能,提高用戶的訪問速度。
面對復雜的服務器集群結構,通常使用彈性計算的方式實現動態擴展各分支,也就是所謂的云服務或虛擬化方式。
【問題八】:如何上線新應用
按照灰度模型跟新迭代應用:
不能影響用戶訪問當前可用的服務,新應用必須保證無重大bug,上線新應用時要保留舊版本應用,以防不測;
用戶量訪問少的時間,一部分一部分地下線服務器,發布完成再上線,直到之后一臺服務器上線完成;
如果新應用出現問題,應能夠快速回滾至舊應用,通過軟鏈接方式快速實現;
緩存控制(ngx_http_proxy_module)
【1】proxy_pass URL;
位于location, if in location, limit_except中
注意:
-
proxy_pass后的路徑不帶uri時會將location的uri傳遞給后端主機;
-
proxy_pass后的路徑是一個uri時會將location的uri替換為proxy_pass的uri;
-
如果location定義其uri時使用正則表達式的模式,則proxy_pass之后必須不能使用uri;
location /uri/ {
proxy_pass http://host[:port]; #http://host/uri
proxy_pass http://host/new_rui/ #http://host/new_uri/
}
location ~|~* PATTERN {
proxy_pass http://HOST;
}
配置nginx代理的URI資源類型
直接代理至后端服務器可在 / 這個location中proxy;但一般分類不同的資源代理至不同的服務器,如php代理至一臺單獨的服務器。
vi default.conf
#前端服務器處理默認url,不代理 /
location / {
root /usr/share/nginx/html; #注釋與否都想
#proxy_pass http://192.168.22.2/;
}
#代理uri至后端主機
location /admin/ {
proxy_pass http://192.168.22.2/; #替換uri
}
#資源的動靜分離
location ~* \.(jpg|jpeg|gif|png)$ {
proxy_pass http://192.168.22.2; #不允許加 / 否則syntax errror
}
location ~* \.(php|phps)$ {
proxy_pass http://192.168.22.3; #不允許加 / 否則syntax errror
}
【2】proxy_set_header field value;設定發往后端主機的請求報文首部的值
默認代理服務為請求者,后端主機記錄的訪問者全是代理服務器。
Nginx增加X-Forwarded-For
記錄客戶端IP,二層代理能夠獲取到$proxy_add_x_forwarded_for
的值,因此不再添加X-Real-IP
值
增加X-Forwarded-For值
server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#后端httpd
vi httpd.conf
LogFormat 中用 %{X-Real_IP} 替換 %h
【3】proxy_cache zone | off;調用path定義在磁盤中的cache zone,開啟或關閉proxy的緩存,在http, server, location定義
【4】proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
key的值為URL的hash值,value為URL資源內容的hash值,兩者之間通過對應關系快速完成URL的查找與組合。
【5】proxy_cache_key string;緩存中用作key的內容,默認為完整的請求URL,但是http與https不能共享緩存。默認值為proxy_cache_key $scheme$proxy_host$request_uri;
可只用后面一截使之更加適用。
【6】proxy_cache_valid [code …] time;定義對特定響應碼的響應內容的緩存時長;
啟用proxy緩存URL資源
vi nginx.conf
http{
proxy_cache_path /var/cache/nginx/proxy_cache levels=1:2:1 keys_zone=pxy_zone:20m max_size=1g;
}
vi default.conf
location ~* \.(jpg|jpeg|gif|png)$ {
proxy_pass http://192.168.22.2; #不允許加 / 否則syntax errror
proxy_cache pxy_zone;
proxy_cache_key $request_uri; #不對scheme分類,更通用
proxy_cache_valid 200 301 302 30m;
proxy_cache_valid any 1m;
}
【7】proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off …;
允許代理服務器用緩存短暫直接響應客戶端,當代理服務器與后端服務器的通信出現以上的錯誤響應碼時。
【8】proxy_connect_timeout time;與后端服務器通信的超時時長,默認為60s,最大不超過75s;同proxy_read_timeout;proxy_send_timeout
Defines a timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.避免響應為502 bad gateway
:
【9】proxy_cache_methods GET | HEAD | POST …;默認GET、HEAD方法的請求才查緩存。
【10】proxy_buffers number size;內存緩沖優化性能,同proxy_buffering;proxy_buffer
-
proxy_buffers 8 4k|8k;
【11】proxy_hide_header field;指明對客戶端隱藏的herder
封裝首部(ngx_http_headers_module)
The ngx_http_headers_module module allows adding the “Expires” and “Cache-Control” header fields, and arbitrary fields, to a response header.
由代理服務器向客戶端響應的header中添加自定義首部,或修改首部的值
【1】add_header name value [always];在header中添加自定義首部響應給客戶端。
如添加這是由代理服務器響應的信息。
-
add_header X-Via $server_addr;
-
add_header X-Accel $server_name;
響應報文首部添加代理信息
default.conf
server {
add_herder X-Via $server_addr;
add_herder X-Accel $server_name;
}
#客戶端瀏覽器顯示的Header中
X-Via:10.1.0.6
X-Accel
【2】expires [modified] time;expires epoch | max | off;定義Expire或Cache-Control首部的值,以實現修改原始服務器定義的緩存時長的目的;
如帶Cookie的圖片不能被緩存,只有修改為public的才能緩存,通過修改過private為public即可被緩存。
代理調度模塊(ngx_http_upstream_module)
The ngx_http_upstream_module module is used to define groups of servers that can be referenced by the proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, and memcached_pass directives.
定義服務器組,不再是代理給單一的服務器,以實現負載均衡。代理時目標為定義的組名。定義組時不能定義協議,只是ip地址的組合。
【1】upstream name { … }
定義后端服務器組,會引入一個新的上下文,定義在http{}中;
upsteam websrv {
server
server
……
}
【2】server address [parameters];
在upstream中定義后端服務器成員,以及相關參數
address:
-
unix:/PATH/TOSOME_SOCK_FILE
-
IP[:port]
-
HOSTNAME[:PORT]
paramenter:
-
weight
=number
sets the weight of the server, by default, 1. -
max_fails
=number
失敗嘗試的次數,超過嘗試的server將被標記為不可用 -
fail_timeout
=time
將服務器標記為不可用狀態的超時累積時長,默認10s -
max_conns
=number
當前最大并發連接數 -
backup
將server標記為“備用”,所有在線的服務器不可用時將啟用 -
down
標記為down,灰色更新
【3】least_conn;最少連接調度算法,當server擁有不同的權重時其為wlc;
【4】least_time header | last_byte;最短平均響應時長和最少連接調度算法;
【4】ip_hash;原地址hash調度方法,將同一客戶端ip的請求始終發往同一server,實現持久連接;
【4】hash key [consistent];對指定的key的hash表實現請求的調度,此處的key為文本、變量或二者的組合;可使用consistent做一致性hash,默認為取模hash;作為緩存服務器應該打開consistent
,避免取模hash錯亂。Embedded Variables有許多可做hash的變量。
對請求報文hash的作用是將請求分類,發往緩存服務器,以提高命中率;
-
hash $request_uri consistent;對請求為key,內容作為value的hash,將始終發往同一后端服務器
-
hash $remote_addr;同一ip始終發往一后端服務器
-
hash $cookie_name;來自同一Browser的訪問發往同一后端服務器,以實現基于cookie的
session sticky
【5】keepalive connections;定義代理服務器與upstream servers長連接的個數,使用長連接以應對并發時的資源不足,但每個worker進程不能有太多的長連接,需手動定義空閑的長連接個數。
【6】health_check [parameters];
定義對后端主機的健康狀態檢測機制;只能用于location
上下文;
可用參數:
-
interval=time:檢測頻率,默認為每隔5秒鐘;
-
fails=number:判斷服務器狀態轉為失敗需要檢測的次數;
-
passes=number:判斷服務器狀態轉為成功需要檢測的次數;
-
uri=uri:判斷其健康與否時使用的uri;
-
match=name:基于指定的match來衡量檢測結果的成?。?/p>
-
port=number:使用獨立的端口進行檢測;
僅Nginx Plus有效;
【7】 match name { … }
Defines the named test set used to verify responses to health check requests.
定義衡量某檢測結果是否為成功的衡量機制;
專用指令:
-
status:期望的響應碼;
status CODE
status ! CODE
… -
header:基于響應報文的首部進行判斷
header HEADER=VALUE
header HEADER ~ VALUE
… -
body:基于響應報文的內容進行判斷
body ~ “PATTERN”
body !~ “PATTERN”
僅Nginx Plus有效;
RR輪詢Upstream Server
vi /etc/nginx/nginx.conf
http{
#定義Upstream服務器組
upstream websrv{
server 192.168.22.2;
server 192.168.22.3:8080;
}
}
default.conf
location / {
proxy_pass http://websrv; #root失效,proxy_pass優先級高
}
#測試:默認調度算法為RR
WRR加權輪詢Upstream Server
http{
upstream websrv{
server 192.168.22.2 weight=2; #2:1
server 192.168.22.3:8080;
}
}
自動識別不可用主機
http{
upstream websrv{
server 192.168.22.2 weight=2 max_fails=2;
server 192.168.22.3:80;
}
}
#手動停用第一臺服務器
設置備用主機
http{
upstream websrv{
server 192.168.22.2 weight=2 max_fails=2;
server 192.168.22.3:80;
server 192.168.22.3:8080 backup;
}
}
#stop或標記為down
#server 192.168.22.2 down;
傳輸層反代(ngx_stream_core_module)
模擬傳輸層反代tcp或udp協議的服務,相當于工作在傳輸層的lvs調度器;
stream{
單獨的stream段,先注釋http段;
}
【1】stream { … }
【2】listen address:port [ssl] [udp] [proxy_protocol] [backlog=number] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
傳輸層反代ssh服務配置
vi nginx.conf
#注釋http段,保留inclued;
include /etc/nginx/conf.d/*.conf;
vi conf.d/sshsrv.conf
stream{
upstream sshsrv {
server 192.168.22.2:22;
server 192.168.22.3:22;
least_conn;
}
server {
listen 10.1.253.6:8520;
proxy_pass sshsrv;
}
}
#分別測試幾種不同的調度算法:
least_conn;
least_time;
ip_hash;
hash $remote_addr;
hash $request_uri consistent; #一致性hash
hash $cookie_name;
總結
Nginx作為反代服務器調度后端的緩存服務器是一種常用的應用層負載均衡方案,其對URL和后端服務器IP做多salt的一致性hash算法以及針對不同URL的分類請求能夠有效解決大量并發時的負載均衡問題。此外,Nginx反代時開啟緩存功能可以大大減小后端服務器的壓力。Nginx不僅僅是個輕量化的Web服務器,更為強大的代理調度功能體現出Nginx的卓越性能。著名的開源項目Tengine、OpenResty都是基于Nginx的改進和擴展,尤其是支持動態語言腳本Lua使得Nginx異常強大。
原創文章,作者:helloc,如若轉載,請注明出處:http://www.www58058.com/55657