nginx 做反向代理時,后端主機有多臺,可以使用 upstream 定義一個后端主機池,在反向代理時直接使用主機池的名字。在 upstream 中可以定義負載均衡調度算法,權重,健康狀態檢測等參數。
例如:
1
2
3
4
|
upstream backend { server 172.16.0.1:80 weight=1 max-fails=3 fail_timeout=10; server 172.16.0.2:80 weight=1max-fails=3 fail_timeout=10;; } |
默認請求下,使用 round-robin 調度算法,并有健康狀態檢查和恢復主機的能力。
ningx 還可以使用這些算法:
- ip_hash:基于源地址哈希,主要目的是會話保持
- least_conn:基于最少活動連接進行調度
- sticky:基于 cookie 進行會話綁定,nginx 會在客戶端第一次訪問時插入路由信息到 cookie 中,或者選擇 cookie 中的某個字段的值作為鍵,以后每次請求將基于此信息進行調度
基于 cookie 的會話綁定共有 cookie,route 和 learn 三種。
例如,基于 cookie name 的調度:
1
2
3
4
5
6
|
upstream backend { server backend1.example.com; server backend2.example.com; sticky cookie srv_id expires=1h domain=.example.com path=/; } |
使用此主機組進行反向代理:
1
2
3
4
5
|
location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_haeder X-Forwared-For $proxy_add_x_forwarded_for; } |
proxy_pass URL 指定代理的后端主機,可以指定 "http" 或 "https" 協議,域名可以是 ip 地址,也可以是 upstream 池名字
- 如果代理指定的是一個 URI 地址,如 http://127.0.0.1/remote,那么將直接被代理至指定 URI,無論請求的 URI 是什么
- 如果代理指定的一個主機而沒有 URI,如 http://127.0.0.1,客戶端請求的URI將被傳遞至指定域名
- 如果 location 中使用模式匹配 url,那么 url 也會被傳遞至代理 URL 的末端
- 如果 location 中使用了 URI 重寫,那么 proxy_pass 會使用重寫后的結果進行處理
proxy_set_header HEADER VALUE 對轉發的報文首部進行修改一些
反向代理時的緩存相關設定
proxy_cache_path PATH [levels=levels] keys_zone=NAME:SIZE
定義磁盤緩存路徑,nignx 的緩存是以鍵值方式存放的,keys_zone 用于指定鍵存放的內存空間名字和大小,對應的值則存放在 PATH 指定的路徑中。levels 可以指定緩存存放路徑的級數和名稱字符數。此設置只能在 http 段中定義。
如:
1
|
proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=one:10m; |
proxy_cache_valid [code ...] time 指定不同響應碼的內容的緩存時間
如:
1
2
3
|
proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m; proxy_cache_valid any 1m; |
proxy_cache_method METHOD 定義哪些方法的請求結果可以被緩存,如:
1
2
|
proxy_cache_method GET; proxy_cache_method HEAD; |
proxy_cache NAME 指定使用預先定義的緩存空間用于緩存
一些問題的解決方法
下面來看看采用Nginx負載均衡之后碰到的問題:
通常解決服務器負載問題,都會通過多服務器分載來解決。常見的解決方案有:
那我們看看Nginx是如何實現負載均衡的,Nginx的upstream目前支持以下幾種方式的分配
1、輪詢(默認)
每個請求按時間順序逐一分配到不同的后端服務器,如果后端服務器down掉,能自動剔除。
2、weight
指定輪詢幾率,weight和訪問比率成正比,用于后端服務器性能不均的情況。
2、ip_hash
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決session的問題。
3、fair(第三方)
按后端服務器的響應時間來分配請求,響應時間短的優先分配。
4、url_hash(第三方)
按訪問url的hash結果來分配請求,使每個url定向到同一個后端服務器,后端服務器為緩存時比較有效。
Upstream配置如何實現負載
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
http { upstream www.test1.com { ip_hash; server 172.16.125.76:8066 weight=10; server 172.16.125.76:8077 down; server 172.16.0.18:8066 max_fails=3 fail_timeout=30s; server 172.16.0.18:8077 backup; } upstream www.test2.com { server 172.16.0.21:8066; server 192.168.76.98:8066; } server { listen 80; server_name www.test1.com; location /{ proxy_pass http://www.test1.com; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { listen 80; server_name www.test2.com; location /{ proxy_pass http://www.test2.com; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } |
當有請求到www.test1.com/www.test2.com 時請求會被分發到對應的upstream設置的服務器列表上。test2的每一次請求分發的服務器都是隨機的,就是第一種情況列舉的。而test1剛是根據來訪問ip的hashid來分發到指定的服務器,也就是說該IP的請求都是轉到這個指定的服務器上。
根據服務器的本身的性能差別及職能,可以設置不同的參數控制。
down 表示負載過重或者不參與負載
weight 權重過大代表承擔的負載就越大
backup 其它服務器時或down時才會請求backup服務器
max_fails 失敗超過指定次數會暫停或請求轉往其它服務器
fail_timeout 失敗超過指定次數后暫停時間
以上就Nginx的負載均衡的簡單配置。那繼續我們的本節討論內容:
一、Session問題
當我們確定一系列負載的服務器后,那我們的WEB站點會分布到這些服務器上。這個時候如果采用Test2 每一次請求隨機訪問任何一臺服務器上,這樣導致你訪問A服務器后,下一次請求又突然轉到B服務器上。這個時候與A服務器建立的Session,傳到B站點服務器肯定是無法正常響應的。我們看一下常用的解決方案:
- Session或憑據緩存到獨立的服務器
- Session或憑據保存數據庫中
- nginx ip_hash 保持同一IP的請求都是指定到固定的一臺服務器
第一種緩存的方式比較理想,緩存的效率也比較高。但是每一臺請求服務器都去訪問Session會話服務器,那不是加載重了這臺Session服務器的負擔嗎?
第二種保存到數據庫中,除了要控制Session的有效期,同時加重了數據庫的負擔,所以最終的轉變為SQL Server 負載均衡,涉及讀,寫,過期,同步。
第三種通過nginx ip_hash負載保持對同一服務器的會話,這種看起來最方便,最輕量。
正常情況下架構簡單的話, ip_hash可以解決Session問題,但是我們來看看下面這種情況
這個時候ip_hash 收到的請求都是來自固定IP代理的請求,如果代理IP的負載過高就會導致ip_hash對應的服務器負載壓力過大,這樣ip_hash就失去了負載均衡的作用了。
如果緩存可以實現同步共享的話,我們可以通過多session服務器來解決單一負載過重的問題。那Memcached是否可以做Session緩存服務器呢?MemcachedProvider提供了Session的功能,即將Session保存到數據庫中。那為什么不直接保存到數據庫中,而要通過Memcached保存到數據庫中呢?很簡單,如果直接保存到數據庫中,每一次請求Session有效性都要回數據庫驗證一下。其次,即使我們為數據庫建立一層緩存,那這個緩存也無法實現分布式共享,還是針對同一臺緩存服務器負載過重。網上也看到有用Memcached實現Session緩存的成功案例,當然數據庫方式實現的還是比較常用的,比如開源Disuz.net論壇。緩存實現的小范圍分布式也是比較常用的,比如單點登錄也是一種特殊情況。
二、文件上傳下載
如果實現了負載均衡,除了Session問題,我們還會碰到文件的上傳下載問題。文件不可能上傳不同的服務器上,這樣會導致下載不到對應文件的問題。我們看一下下面的方案
- 獨立文件服務器
- 文件壓縮數據庫
兩種方案都是常用的,我們來說一下文件壓縮數據庫,以前的方式都是將文件二進制壓縮至關系型數據庫,而現在NOSQL的流行,加上MongoDB處理文件又比較方便,所以文件壓庫又多了一種選擇。畢竟文件服務器的效率和管理以及安全都不及數據庫。
隨便聊聊這點事,其實也就是一些應用的趨勢和多一種解決方案的實現。