介紹
在多個(gè)應(yīng)用實(shí)例間做負(fù)載均衡是一個(gè)被廣泛使用的技術(shù),用于優(yōu)化資源效率,最大化吞吐量,減少延遲和容錯(cuò)。
負(fù)載均衡的幾個(gè)重要的點(diǎn):
- 上游服務(wù)器配置 :使用upstream server配置多個(gè)上游服務(wù)器。
- 負(fù)載均衡算法 :配置多個(gè)上游服務(wù)器時(shí)的負(fù)載均衡機(jī)制。
- 失敗重試機(jī)制 :配置當(dāng)超時(shí)或上游服務(wù)器不存活時(shí),是否需要重試其他上游服務(wù)器。
- 服務(wù)器心跳檢查 :上游服務(wù)器的健康檢查/心跳檢查。
負(fù)載均衡方法
請(qǐng)求轉(zhuǎn)發(fā)的策略。
- round-robin/輪詢(xún)(默認(rèn)): 到應(yīng)用服務(wù)器的請(qǐng)求以round-robin/輪詢(xún)的方式被分發(fā)
- least-connected/最少連接:下一個(gè)請(qǐng)求將被分派到活動(dòng)連接數(shù)量最少的服務(wù)器
- ip-hash/IP散列: 使用hash算法來(lái)決定下一個(gè)請(qǐng)求要選擇哪個(gè)服務(wù)器(基于客戶(hù)端IP地址)
Nginx 配置
1. 基本配置
http {
upstream myapp1 {
// 負(fù)載均衡的方法,默認(rèn)為 round-robin
// least_conn;
// ip_hash;
// 均衡的服務(wù)器清單
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
server {
listen 80;
location / {
proxy_pass http://myapp1;
}
}
}
2. 權(quán)重配置
可以更具服務(wù)器的性能,進(jìn)行權(quán)重的配置
upstream myapp1 {
server srv1.example.com weight=3;
server srv2.example.com;
server srv3.example.com;
}
在這個(gè)配置中,每5個(gè)新請(qǐng)求將會(huì)如下的在應(yīng)用實(shí)例中分派: 3個(gè)請(qǐng)求分派去srv1,一個(gè)去srv2,另外一個(gè)去srv3.
在最近的nginx版本中,可以類(lèi)似的在最少連接和IP哈希負(fù)載均衡中使用權(quán)重。
健康檢測(cè)
Upstream 服務(wù)器節(jié)點(diǎn)的健康檢測(cè) 及 分發(fā)控制。
被動(dòng)能做到訪問(wèn)那一刻失敗的,可以重新分發(fā)到其他的節(jié)點(diǎn);主動(dòng)可以提前做好監(jiān)測(cè),避免錯(cuò)誤碰撞。
1. 被動(dòng)的健康檢測(cè)
通過(guò) nginx 自帶的 ngx_http_proxy_module 模塊 和 ngx_http_upstream_module模塊進(jìn)行檢測(cè),對(duì)具體的請(qǐng)求結(jié)果分析,判斷該服務(wù)器節(jié)點(diǎn)是否有問(wèn)題。
http {
upstream myapp1 {
// ngx_http_upstream_module。失敗1次后,即停止向該服務(wù)器分發(fā),直到 10秒后重新分發(fā)
server srv1.example.com weight=1 max_fails=1 fail_timeout=10;
server srv2.example.com weight=1 max_fails=1 fail_timeout=10;
server srv3.example.com weight=1 max_fails=1 fail_timeout=10;
}
server {
listen 80;
// ngx_http_proxy_module
proxy_send_timeout=60;
proxy_connect_timeout=60;
proxy_read_timeout=60;
proxy_next_upstream=error timeout; // 默認(rèn)
location / {
proxy_pass http://myapp1;
}
}
}
- fail_timeout — 該參數(shù)表示停止分發(fā)請(qǐng)求至該應(yīng)用服務(wù)器的時(shí)間
- max_fails — 設(shè)置訪問(wèn)失敗的最大次數(shù)
- proxy_connect_timeout - 設(shè)置與后端服務(wù)器建立連接的超時(shí)時(shí)間
- proxy_read_timeout - 定義從后端服務(wù)器讀取響應(yīng)的超時(shí)。此超時(shí)是指相鄰兩次讀操作之間的最長(zhǎng)時(shí)間間隔,而不是整個(gè)響應(yīng)傳輸完成的最長(zhǎng)時(shí)間。如果后端服務(wù)器在超時(shí)時(shí)間段內(nèi)沒(méi)有傳輸任何數(shù)據(jù),連接將被關(guān)閉
- proxy_next_upstream - 指定在何種情況下一個(gè)失敗的請(qǐng)求應(yīng)該被發(fā)送到下一臺(tái)后端服務(wù)器
error # 和后端服務(wù)器建立連接時(shí),或者向后端服務(wù)器發(fā)送請(qǐng)求時(shí),或者從后端服務(wù)器接收響應(yīng)頭時(shí),出現(xiàn)錯(cuò)誤
timeout # 和后端服務(wù)器建立連接時(shí),或者向后端服務(wù)器發(fā)送請(qǐng)求時(shí),或者從后端服務(wù)器接收響應(yīng)頭時(shí),出現(xiàn)超時(shí)
invalid_header # 后端服務(wù)器返回空響應(yīng)或者非法響應(yīng)頭
http_500 # 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為500
http_502 # 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為502
http_503 # 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為503
http_504 # 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為504
http_404 # 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為404
off # 停止將請(qǐng)求發(fā)送給下一臺(tái)后端服務(wù)器
出現(xiàn) error 的場(chǎng)景,常見(jiàn)的是上游服務(wù)器的服務(wù)重啟、停止,或者異常崩潰導(dǎo)致的無(wú)法提供正常服務(wù)。而 timeout 的情況,就是代理請(qǐng)求過(guò)程中達(dá)到對(duì)應(yīng)的超時(shí)配置,主要包括了:
proxy_connect_timeout,建立三次握手的時(shí)間
proxy_read_timeout,建立連接后,等待上游服務(wù)器響應(yīng)以及處理請(qǐng)求的時(shí)間
proxy_send_timeout,數(shù)據(jù)回傳的間隔時(shí)間(注意不是數(shù)據(jù)發(fā)送耗時(shí))
2. 主動(dòng)的健康檢測(cè)
主動(dòng)定時(shí)向每臺(tái)服務(wù)器發(fā)送指令,來(lái)監(jiān)測(cè)是否正常。主要以下兩種:
- nginx_upstream_check_module模塊
- ngx_http_healthcheck_module模塊
2.1 nginx_upstream_check_module模塊
除了自帶的上述模塊,還有一個(gè)更專(zhuān)業(yè)的模塊,來(lái)專(zhuān)門(mén)提供負(fù)載均衡器內(nèi)節(jié)點(diǎn)的健康檢查的。這個(gè)就是淘寶技術(shù)團(tuán)隊(duì)開(kāi)發(fā)的 nginx 模塊。
在淘寶自己的 tengine 上是自帶了該模塊的,大家可以訪問(wèn)淘寶tengine的官網(wǎng)來(lái)獲取該版本的nginx,官方地址:http://tengine.taobao.org/ 。
upstream name {
server 192.168.0.21:80;
server 192.168.0.22:80;
// 對(duì)所有節(jié)點(diǎn),每個(gè)3秒檢測(cè)一次,請(qǐng)求2次正常則標(biāo)記 realserver狀態(tài)為up,如果檢測(cè) 5 次都失敗,則標(biāo)記 realserver的狀態(tài)為down,超時(shí)時(shí)間為1秒
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
// check_http_send "HEAD /status HTTP/1.0\r\n\r\n";
// check_http_expect_alive http_2xx http_3xx;
}
參數(shù)
- interval:向后端發(fā)送的健康檢查包的間隔。
- fall(fall_count): 如果連續(xù)失敗次數(shù)達(dá)到fall_count,服務(wù)器就被認(rèn)為是down。
- rise(rise_count): 如果連續(xù)成功次數(shù)達(dá)到rise_count,服務(wù)器就被認(rèn)為是up。
- timeout: 后端健康請(qǐng)求的超時(shí)時(shí)間。
- default_down: 設(shè)定初始時(shí)服務(wù)器的狀態(tài),如果是true,就說(shuō)明默認(rèn)是down的,如果是false,就是up的。默認(rèn)值是true,也就是一開(kāi)始服務(wù)器認(rèn)為是不可用,要等健康檢查包達(dá)到一定成功次數(shù)以后才會(huì)被認(rèn)為是健康的。
- type:健康檢查包的類(lèi)型,現(xiàn)在支持以下多種類(lèi)型
- tcp:簡(jiǎn)單的tcp連接,如果連接成功,就說(shuō)明后端正常。
- ssl_hello:發(fā)送一個(gè)初始的SSL hello包并接受服務(wù)器的SSL hello包。
- http:發(fā)送HTTP請(qǐng)求,通過(guò)后端的回復(fù)包的狀態(tài)來(lái)判斷后端是否存活。
- mysql: 向mysql服務(wù)器連接,通過(guò)接收服務(wù)器的greeting包來(lái)判斷后端是否存活。
- ajp:向后端發(fā)送AJP協(xié)議的Cping包,通過(guò)接收Cpong包來(lái)判斷后端是否存活。
- port: 指定后端服務(wù)器的檢查端口。你可以指定不同于真實(shí)服務(wù)的后端服務(wù)器的端口,比如后端提供的是443端口的應(yīng)用,你可以去檢查80端口的狀態(tài)來(lái)判斷后端健康狀況。默認(rèn)是0,表示跟后端server提供真實(shí)服務(wù)的端口一樣。該選項(xiàng)出現(xiàn)于Tengine-1.4.0
健康服務(wù)器查看頁(yè)面
在Tengine-1.4.0以后,你可以配置顯示頁(yè)面的格式。支持的格式有: html、csv、 json。默認(rèn)類(lèi)型是html。
你也可以通過(guò)請(qǐng)求的參數(shù)來(lái)指定格式,假設(shè)‘/status’是你狀態(tài)頁(yè)面的URL, format參數(shù)改變頁(yè)面的格式,比如:
/server_status?format=html
/server_status?format=csv
/server_status?format=json
同時(shí)你也可以通過(guò)status參數(shù)來(lái)獲取相同服務(wù)器狀態(tài)的列表,比如:
/server_status?format=html&status=down
/server_status?format=csv&status=up
下面是一個(gè)狀態(tài)也配置的范例:
http {
server {
// 定義服務(wù)器的查看地址(可以通過(guò)程序靈活控制當(dāng)前機(jī)器的工作狀態(tài))
location /server_status {
check_status;
access_log off;
}
}
}
2.2 health_check
只有商業(yè)版的 nginx 可用,這里不作詳細(xì)介紹。主要是通過(guò) health_check + zone 指令
http {
upstream onmpw {
// zone指令定義了一塊兒內(nèi)存空間。這塊兒空間存儲(chǔ)在各個(gè)工作進(jìn)程中共享的運(yùn)行環(huán)境的狀態(tài)和應(yīng)用服務(wù)器組的配置信息
zone onmpw 64k;
server 192.168.144.128;
server 192.168.144.132;
server 192.168.144.131;
}
server {
listen 80;
location / {
proxy_pass http://onmpw;
// interval=10: 每隔10秒向服務(wù)器發(fā)送一個(gè) / http 請(qǐng)求,如果沒(méi)有返回 2xx 或者 3xx,都認(rèn)為失敗
// fails=3: 如果請(qǐng)求失敗次數(shù)達(dá)到3次,則該應(yīng)用服務(wù)器被認(rèn)為不能訪問(wèn)
// passes=2: 被認(rèn)定為不能訪問(wèn)的服務(wù)器需要再次進(jìn)行兩次health_check 以后才會(huì)再次被認(rèn)為是可以正常訪問(wèn)的
// uri=/some/path: 指定 health check 的地址
health_check interval=10 fails=3 passes=2;
}
}
}
Upstream 的動(dòng)態(tài)負(fù)載均衡
我們可以發(fā)現(xiàn),如果每次upstream列表有改動(dòng),都需要到服務(wù)器進(jìn)行修改。
更好的做法是實(shí)現(xiàn)upstream服務(wù)的自動(dòng)發(fā)現(xiàn)。
Consul是一款開(kāi)源的分布式服務(wù)注冊(cè)與發(fā)現(xiàn)系統(tǒng),通過(guò)HTTP API可以使得服務(wù)注冊(cè)、發(fā)現(xiàn)實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單,它支持如下特性。
- 服務(wù)注冊(cè): 服務(wù)實(shí)現(xiàn)者可以通過(guò)HTTP API或DNS方式,將服務(wù)注冊(cè)到Consul。
- 服務(wù)發(fā)現(xiàn): 服務(wù)消費(fèi)者可以通過(guò)HTTP API或DNS方式,從Consul獲取服務(wù)的IP 和PORT。
- 故障檢測(cè): 支持如TCP、HTTP等方式的健康檢查機(jī)制,從而當(dāng)服務(wù)有故障時(shí)自動(dòng)摘除。
- K/V存儲(chǔ): 使用K/V存儲(chǔ)實(shí)現(xiàn)動(dòng)態(tài)配置中心,其使用HTTP長(zhǎng)輪詢(xún)實(shí)現(xiàn)變更觸發(fā)和配置更改。
- 多數(shù)據(jù)中心: 支持多數(shù)據(jù)中心,可以按照數(shù)據(jù)中心注冊(cè)和發(fā)現(xiàn)服務(wù),即支持只消費(fèi)本地機(jī)房服務(wù),使用多數(shù)據(jù)中心集群還可以避免單數(shù)據(jù)中心的單點(diǎn)故障。
- Raft算法: Consul使用Raft算法實(shí)現(xiàn)集群數(shù)據(jù)一致性。
通過(guò)Consul可以管理服務(wù)注冊(cè)與發(fā)現(xiàn),接下來(lái)需要有一個(gè)與Nginx部署在同一臺(tái)機(jī)器的Agent來(lái)實(shí)現(xiàn)Nginx配置更改和Nginx重啟功能。
有Confd或者Consul-template兩個(gè)選擇,而Consul-template是Consul官方提供的。其使用HTTP長(zhǎng)輪詢(xún)實(shí)現(xiàn)變更觸發(fā)和配置更改(使用Consul的watch命令實(shí)現(xiàn))。也就是說(shuō),我們使用Consul-template實(shí)現(xiàn)配置模板,然后拉取Consul配置渲染模板來(lái)生成Nginx實(shí)際配置。
參考
《Using nginx as HTTP load balancer》官網(wǎng)
《使用nginx實(shí)現(xiàn)HTTP負(fù)載均衡》 翻譯官網(wǎng)