負載均衡在服務端開發中算是一個比較重要的特性。因為Nginx除了作為正常的Web伺服器外,還會被大規模的用于反向代理前端,因為Nginx的異步架構可以處理很大的并發請求,把這些并發請求hold住之後就可以分發給背景服務端(backend servers, 後面簡稱backend)來做複雜的計算、處理和響應,并且在業務量增加的時候可以友善地擴容背景伺服器。
負載均衡可以分為硬體負載均衡和軟體負載均衡,前者一般是專用的軟體和硬體相結合的裝置,裝置商會提供完整成熟的解決方案,通常也會更加昂貴。軟體的複雜均衡以Nginx占據絕大多數,本文也是基于其手冊做相應的學習研究的。
一、基本簡介
負載均衡涉及到以下的基礎知識。
(1) 負載均衡算法
a. Round Robin: 對所有的backend輪訓發送請求,算是最簡單的方式了,也是預設的配置設定方式;
b. Least Connections(least_conn): 跟蹤和backend目前的活躍連接配接數目,最少的連接配接數目說明這個backend負載最輕,将請求配置設定給他,這種方式會考慮到配置中給每個upstream配置設定的weight權重資訊;
c. Least Time(least_time): 請求會配置設定給響應最快和活躍連接配接數最少的backend;
d. IP Hash(ip_hash): 對請求來源IP位址計算hash值,IPv4會考慮前3個octet,IPv6會考慮所有的位址位,然後根據得到的hash值通過某種映射配置設定到backend;
e. Generic Hash(hash): 以使用者自定義資源(比如URL)的方式計算hash值完成配置設定,其可選consistent關鍵字支援一緻性hash特性;
(2) 會話一緻性
使用者(浏覽器)在和服務端互動的時候,通常會在本地儲存一些資訊,而整個過程叫做一個會話(Session)并用唯一的Session ID進行辨別。會話的概念不僅用于購物車這種常見情況,因為HTTP協定是無狀态的,是以任何需要邏輯上下文的情形都必須使用會話機制,此外HTTP用戶端也會額外緩存一些資料在本地,這樣就可以減少請求提高性能了。如果負載均衡可能将這個會話的請求配置設定到不同的背景服務端上,這肯定是不合适的,必須通過多個backend共享這些資料,效率肯定會很低下,最簡單的情況是保證會話一緻性——相同的會話每次請求都會被配置設定到同一個backend上去。
(3) 背景服務端的動态配置
出問題的backend要能被及時探測并剔除出配置設定群,而當業務增長的時候可以靈活的添加backend數目。此外目前風靡的Elastic Compute雲計算服務,服務商也應當根據目前負載自動添加和減少backend主機。
(4) 基于DNS的負載均衡
通常現代的網絡服務者一個域名會關連到多個主機,在進行DNS查詢的時候,預設情況下DNS伺服器會以round-robin形式以不同的順序傳回IP位址清單,是以天然将客戶請求配置設定到不同的主機上去。不過這種方式含有固有的缺陷:DNS不會檢查主機和IP位址的可通路性,是以配置設定給用戶端的IP不確定是可用的(Google 404);DNS的解析結果會在用戶端、多個中間DNS伺服器不斷的緩存,是以backend的配置設定不會那麼的理想。
二、Nginx中的負載均衡
Nginx中的負載均衡配置在手冊中描述的極為細緻。對于常用的HTTP負載均衡,主要先定義一個upstream作為backend group,然後通過proxy_pass/fastcgi_pass等方式進行轉發操作,其中fastcgi_pass幾乎算是Nginx+PHP站點的标配了。
2.1 會話一緻性
Nginx中的會話一緻性是通過sticky開啟的,會話一緻性和之前的負載均衡算法之間并不沖突,隻是需要在第一次配置設定之後,該會話的所有請求都配置設定到那個相同的backend上面。目前支援三種模式的會話一緻性:
(1). Cookie Insertion
在backend第一次response之後,會在其頭部添加一個session cookie,之後用戶端接下來的請求都會帶有這個cookie值,Nginx可以根據這個cookie判斷需要轉發給哪個backend了。
sticky
上面的srv_id代表了cookie的名字,而後面的參數expires、domain、path都是可選的。
(2). Sticky Routes
也是在backend第一次response之後,會産生一個route資訊,route資訊通常會從cookie/URI資訊中提取。
sticky route $route_cookie $route_uri;
這樣Nginx會按照順序搜尋$route_cookie、$route_uri參數并選擇第一個非空的參數用作route,而如果所有的參數都是空的,就使用上面預設的負載均衡算法決定請求分發給哪個backend。
(3). Learn
較為的複雜也較為的智能,Nginx會自動監測request和response中的session資訊,而且通常需要回話一緻性的請求、應答中都會帶有session資訊,這和第一種方式相比是不用增加cookie,而是動态學習已有的session。
這種方式需要使用到zone結構,在Nginx中zone都是共享記憶體,可以在多個worker process中共享資料用的。(不過其他的會話一緻性怎麼沒用到共享記憶體區域呢?)
sticky learn
create=$upstream_cookie_examplecookie
lookup=$cookie_examplecookie
zone=client_sessions:1m
timeout=1h;
2.2 Session Draining
主要是有需要關閉某些backend以便維護或者更新,這些關鍵性的服務都講求gracefully處理的:就是新的請求不會發送到這個backend上面,而之前配置設定到這個backend的會話的後續請求還會繼續發送給他,直到這個會話最終完成。
讓某個backend進入draining的狀态,既可以直接修改配置檔案,然後按照之前的方式通過向master process發送信号重新加載配置,也可以采用Nginx的on-the-fly配置方式。
$ curl http://localhost/upstream_conf?upstream=backend
$ curl http://localhost/upstream_conf?upstream=backend&id=1&drain=1
通過上面的方式,先列出各個bacnkend的ID号,然後drain指定ID的backend。通過線上觀測backend的所有session都完成後,該backend就可以下線了。
2.3 backend健康監測
backend出錯會涉及到兩個參數,max_fails=1 fail_timeout=10s;意味着隻要Nginx向backend發送一個請求失敗或者沒有收到一個響應,就認為該backend在接下來的10s是不可用的狀态。
通過周期性地向backend發送特殊的請求,并期盼收到特殊的響應,可以用以确認backend是健康可用的狀态。通過health_check可以做出這個配置。
match server_ok {
status 200-399;
header Content-Type = text/html;
body !~ "maintenance mode";
}
server {
location / {
proxy_pass http://backend;
health_check interval=10 fails=3 passes=2 match=server_ok;
}
}
上面的health_check是必須的,後面的參數都是可選的。尤其是後面的match參數,可以自定義伺服器健康的條件,包括傳回狀态碼、頭部資訊、傳回body等,這些條件是&&與關系。預設情況下Nginx會相隔interval的間隔向backend group發送一個”/“的請求,如果逾時或者傳回非2xx/3xx的響應碼,則認為對應的backend是unhealthy的,那麼Nginx會停止向其發送request直到下次改backend再次通過檢查。
在使用了health)check功能的時候,一般都需要在backend group開辟一個zone,在共享backend group配置的同時,所有backend的狀态就可以在所有的worker process所共享了,否則每個worker process獨立儲存自己的狀态檢查計數和結果,兩種情況會有很大的差異哦。
2.4 通過DNS設定HTTP負載均衡
Nginx的backend group中的主機可以配置成域名的形式,如果在域名的後面添加resolve參數,那麼Nginx會周期性的解析這個域名,當域名解析的結果發生變化的時候會自動生效而不用重新開機。
http {
resolver 10.0.0.1 valid=300s ipv6=off;
resolver_timeout 10s;
server {
location / {
proxy_pass http://backend;
}
}
upstream backend {
zone backend 32k;
least_conn;
...
server backend1.example.com resolve;
server backend2.example.com resolve;
}
}
如果域名解析的結果含有多個IP位址,這些IP位址都會儲存到配置檔案中去,并且這些IP都參與到自動負載均衡。
2.5 TCP/UDP流量的負載均衡
除了專長的HTTP負載均衡,Nginx還支援TCP和UDP流量的負載均衡,适用于LDAP/MySQL/RTMP和DNS/syslog/RADIUS各種應用場景。這類情況的負載均衡使用stream來配置,Nginx編譯的時候需要支援–with-stream選項。檢視 手冊 ,其配置原理和參數和HTTP負載均衡差不多。
因為TCP、UDP的負載均衡都是針對通用程式的,是以之前HTTP協定支援的match條件(status、header、body)是沒法使用的。TCP和UDP的程式可以根據特定的程式,采用send、expect的方式來進行動态健康檢測。
match http {
send "GET / HTTP/1.0rnHost: localhostrnrn";
expect ~* "200 OK";
}
2.6 其他特性
slow_start=30s:防止新添加/恢複的主機被突然增加的請求所壓垮,通過這個參數可以讓該主機的weight從0開始慢慢增加到設定值,讓其負載有一個緩慢增加的過程。
max_conns=30:可以設定backend的最大連接配接數目,當超過這個數目的時候會被放到queue隊列中,同時隊列的大小和逾時參數也可以設定,當隊列中的請求數大于設定值,或者超過了timeout但是backend還不能處理請求,則用戶端将會收到一個錯誤傳回。通常來說這還是一個比較重要的參數,因為Nginx作為反向代理的時候,通常就是用于抗住并發量的,如果給backend過多的并發請求,很可能會占用後端過多的資源(比如線程、程序非事件驅動),最終反而會影響backend的處理能力。
三、nginx負責均衡的政策
1、輪詢(預設)
每個請求按時間順序逐一配置設定到不同的後端伺服器,如果後端伺服器down掉,能自動剔除。
upstream backserver {
server 192.168.0.14;
server 192.168.0.15;
}
2、指定權重
指定輪詢幾率,weight和通路比率成正比,用于後端伺服器性能不均的情況。
upstream backserver {
server 192.168.0.14 weight=10;
server 192.168.0.15 weight=10;
}
3、IP綁定 ip_hash
每個請求按通路ip的hash結果配置設定,這樣每個訪客固定通路一個後端伺服器,可以解決session的問題。
upstream backserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
4、fair(第三方)
按後端伺服器的響應時間來配置設定請求,響應時間短的優先配置設定。
upstream backserver {
server server1;
server server2;
fair;
}
5、url_hash(第三方)
按通路url的hash結果來配置設定請求,使每個url定向到同一個後端伺服器,後端伺服器為緩存時比較有效。
upstream
四、在需要使用負載均衡的server中增加
proxy_pass
server 127.0.0.1:9090 down; (down 表示單前的server暫時不參與負載)
server 127.0.0.1:8080 weight=2; (weight 預設為1.weight越大,負載的權重就越大)
server 127.0.0.1:6060;
server 127.0.0.1:7070 backup; (其它所有的非backup機器down或者忙的時候,請求backup機器)
}
max_fails :允許請求失敗的次數預設為1.當超過最大次數時,傳回proxy_next_upstream 子產品定義的錯誤
fail_timeout:max_fails次失敗後,暫停的時間
對标阿裡P6+的Java架構師
擷取更多學習資料,可以加群:473984645或掃描下方二維碼