記錄一下nginx加權分配算法。
nginx可以指定輪詢幾率,weight和訪問比率成正比,用於后端服務器性能不均的情況。
例如:
upstream backend {
server a weight=6;
server b weight=3;
server c weight=1;
}
按照配置,每有10次請求,其中6個會轉發到a服務器,3個轉發到b服務器,1個轉發到c服務器。
每個服務器都有三個權重變量,先解釋下它們的含義。
(1) weight
配置文件中指定的該服務器的權重,這個值是固定不變的。
(2) effective_weight
服務器的有效權重,初始值為weight。
在釋放服務器時,如果發現和某服務器的通信過程中發生了錯誤,就減小它的effective_weight。
此后有新的請求過來時,在選取該服務器的過程中,再逐步增加effective_weight,最終又恢復到weight。
之所以增加這個字段,是為了當服務器發生錯誤時,降低其權重。
(3) current_weight
服務器目前的權重,初始為0,之后會動態調整。
那么如何動態調整呢?
nginx每次選取服務器時:
- 先遍歷集群中所有服務器,將每個服務器的current_weight增加它的effective_weight,
- 再累加所有服務器的effective_weight,保存為total。
- 判斷當前服務器的current_weight是否最大,是則選中該服務器,然后把它的current_weight減去total。不是則不會被選中,current_weight也就不用減了。
弄清了三個weight字段的含義后,加權輪詢算法可描述為:
- 對於每個請求,遍歷集群中的所有可用服務器,對於每個服務器執行:current_weight += effecitve_weight。
- 累加所有effective_weight,保存為total。
- 選出current_weight最大的服務器,作為本次選定的服務器。
- 對於本次選定的服務器,執行:current_weight -= total。
下面以表格形式記錄其過程:
請求次數 | 開始current_weight | 增加effective_weight | 累加total | 選中服務器 | 選中后current_weight |
---|---|---|---|---|---|
1 | [0, 0, 0] | [6, 3, 1] | 10 | a | [-4, 3, 1] |
2 | [-4, 3, 1] | [2, 6, 2] | 10 | b | [2, -4, 2] |
3 | [2, -4, 2] | [8, -1, 3] | 10 | a | [-2, -1, 3] |
4 | [-2, -1, 3] | [4, 2, 4] | 10 | a | [-6, 2, 4] |
5 | [-6, 2, 4] | [0, 5, 5] | 10 | b | [0, -5, 5] |
6 | [0, -5, 5] | [6, -2, 6] | 10 | a | [-4, -2, 6] |
7 | [-4, -2, 6] | [2, 1, 7] | 10 | c | [2, 1, -3] |
8 | [2, 1, -3] | [8, 4, -2] | 10 | a | [-2, 4, -2] |
9 | [-2, 4, -2] | [4, 7, -1] | 10 | b | [4, -3, -1] |
10 | [4, -3, -1] | [10, 0, 0] | 10 | a | [0, 0, 0] |
可以看到,選中服務器依次為[a, b, a, a, b, a, c, a, b, a]。
a,b,c分別被選中了6,3,1次,正好是符合其權重值的;
服務器a雖然權重大,但沒有被連續選取,不會對a服務器連續請求;
經過10次請求后,a,b,c的當前權重current_weight又全部歸0,如此便可循環往復。
ps: 這里我們發現total永遠都是10,因為這里假定服務器都沒有發生故障或返回錯誤,其effective_weight不變。實際中如果服務器發生了錯誤,nginx當然也會進行降權處理,total也會變啦。這里我們學習一下正常算法,出錯的情況就先不展開了。