1 負載均衡的産生
假設你訂閱了一個别人的服務,從注冊中心查詢得到了這個服務的可用節點清單,而這個清單裡包含了幾十個節點,這個時候你該選擇哪個節點發起調用呢?這就是用戶端負載均衡算法的問題。
2 負載均衡算法的意義
- 考慮調用的均勻性,也就是要讓每個節點都接收到調用,發揮所有節點的作用
- 考慮調用的性能,也就是哪個節點響應最快,優先調用哪個節點
不同負載均衡算法也就是在這兩個方面的考慮不同。
3 常見的負載均衡算法
random(随機)
dubbo預設。

從可用的服務節點中,随機挑選一個節點來通路。
實作時,随機算法通常通過生成一個随機數來實作,比如服務有10個節點,那麼就每一次生成一個1~10之間的随機數,假設生成的是2,那麼就通路編号為2的節點。
采用随機算法,在節點數量足夠多,并且通路量比較大的情況下,各個節點被通路的機率基本相同。
适用場景
實作簡單,在請求量遠超可用服務節點數量的情況下,各個服務節點被通路的機率基本相同,主要應用在各個服務節點的性能差異不大時。
roundrobin(輪詢)
均勻地将流量打到各個機器上去,但如果各個機器的性能不一樣,容易導緻性能差的機器負載過高。是以此時需要調整權重,讓性能差的機器承載權重小一些,流量少一些。
按固定順序,把可用的服務節點,挨個通路一次。
類似随機算法,各個服務節點被通路的機率也基本相同,也主要應用在各個服務節點性能差異不大。
實作
通常把所有可用節點放到一個數組,挨個通路。比如服務有10個節點,放到數組裡就是一個大小為10的數組,這樣的話就可以從序号為0的節點開始通路,通路後序号自動加1,下一次就會通路序号為1的節點,以此類推。
輪詢算法能保證所有節點被通路到的機率相同。一個輪詢算法的代碼實作,可以參考這個示例。
權重輪詢
輪詢能夠保證所有節點被通路的機率相同,而權重輪詢算法是在此基礎上,給每個節點賦予一個權重,進而使每個節點被通路到的機率不同,權重大的節點被通路的機率就高,權重小的節點被通路的機率就小。
權重輪詢算法是生成一個節點序列,該序列裡有n個節點,n是所有節點的權重之和。在這個序列中,每個節點出現的次數,就是它的權重值。比如有三個節點:a、b、c,權重分别是3、2、1,那麼生成的序列就是{a、a、b、c、b、a},這樣的話按照這個序列通路,前6次請求就會分别通路節點a三次,節點b兩次,節點c一次。從第7個請求開始,又重新按照這個序列的順序來通路節點。
要盡可能保證生産的序列的均勻,如果生成的不均勻會造成節點通路失衡,比如剛才的例子,如果生成的序列是{a、a、a、b、b、c},就會導緻前3次通路的節點都是a。
主要用在服務節點性能差異比較大的情況。比如新的服務節點的性能往往要高于舊的節點,這個時候可以給新的節點設定更高的權重,讓它承擔更多的請求,充分發揮新節點的性能優勢。
leastactive(最少活躍連接配接)
自動感覺一下,如果某個機器性能越差,那麼接收的請求越少,越不活躍,此時就會給不活躍的性能差的機器更少的請求
每次通路都選擇連接配接數最少的節點。因為不同節點處理請求的速度不同,使得同一個服務消費者同每一個節點的連接配接數都不相同。連接配接數大的節點,可以認為是處理請求慢,而連接配接數小的節點,可以認為是處理請求快。是以在挑選節點時,可以以連接配接數為依據,選擇連接配接數最少的節點通路。
在實作時,需要記錄跟每一個節點的連接配接數,這樣在選擇節點時,才能比較出連接配接數最小的節點。
用戶端同服務端節點的連接配接數是在時刻變化的,理論上連接配接數越少代表此時服務端節點越空閑,選擇最空閑的節點發起請求,能擷取更快的響應速度。
特别是當:
- 服務端節點性能差異較大
- 不好做到預先定義權重
采用該算法很不錯。
consistanthash(一緻性 hash)
一緻性Hash算法,相同參數的請求一定分發到一個provider上去,provider挂掉的時候,會基于虛拟節點均勻配置設定剩餘的流量,抖動不會太大。
通過某個hash函數,把同一個來源的請求都映射到同一個節點上。
同一個來源的請求,隻會映射到同一個節點上,可以說是具有記憶功能,不會有分布式會話的困擾。隻有當該節點不可用時,請求才會被配置設定到相鄰的可用節點上。
如果你需要的不是随機負載均衡,是要一類請求都到一個節點,那就走這個一緻性hash政策。
因為它能夠保證同一個用戶端的請求始終通路同一個服務節點,是以适合服務端節點處理不同用戶端請求差異較大的場景。比如服務端緩存裡儲存着用戶端的請求結果,如果同一用戶端一直通路一個服務節點,那麼就可以一直從緩存中擷取資料。
這五種負載均衡算法是業界最常用的,不光在RPC調用中被廣泛采用,在一些負載均衡元件比如Nginx中也有應用,是以說是一種通用的負載均衡算法,但是不是所有的業務場景都能很好解決呢?
如果:
- 服務節點數量衆多,且性能差異比較大
- 服務節點清單經常發生變化,增加節點或者減少節點時有發生
- 用戶端和服務節點之間的網絡情況比較複雜,有些在一個資料中心,有些不在一個資料中心需要跨網通路,而且網絡經常延遲或者抖動
這時:
- 随機、輪詢,第一個情況就不滿足
- 權重需要預先配置服務節點的權重,在節點清單經常變化的情況下不好維護,是以也不适合
- 最少活躍連接配接算法是從用戶端自身次元去判斷的,在實際應用時,并不能直接反映出服務節點的請求量大小,尤其是在網絡情況比較複雜的情況下,并不能做到動态的把請求發送給最合适的服務節點
- 一緻性hash顯然也不适合這種場景
針對上面這種場景,有一種算法更加适合,這種算法就是
自适應最優選擇算法
在用戶端本地維護一份同每一個服務節點的性能統計快照,每隔一段時間更新該快照。
發起請求時,根據“二八原則”,把服務節點分成兩部分,找出20%響應最慢的節點,降低權重。這樣用戶端就能夠實時的根據自身通路每個節點性能的快慢,動态調整通路最慢的那些節點的權重,來減少通路量,進而可以優化長尾請求。
自适應最優選擇算法是對權重輪詢算法的改良,可以看作是一種動态權重輪詢算法:
每隔一段時間擷取用戶端同每個服務節點之間調用的平均性能統計
需要在記憶體中開辟一塊空間記錄用戶端同每一個服務節點之間調用的平均性能,并每隔一段固定時間去更新。這個更新的時間間隔不能太短,太短的話很容易受瞬時的性能抖動影響,導緻統計變化太快,沒有參考性;同時也不能太長,太長的話時效性就會大打折扣,效果不佳。根據我的經驗,1分鐘的更新時間間隔是個比較合适的值。
按這個性能統計對服務節點進行排序,對排在性能倒數20%的那部分節點賦予一個較低的權重,其餘的節點賦予正常的權重
關鍵是設定權重值,即使服務節點之間的性能差異較大,也不适合把權重設定得差異太大,可能導緻性能較好的節點與性能較差的節點之間調用量相差太大,這樣也不是一種合理的狀态。在實際設定時,可以設定20%性能較差的節點權重為3,其餘節點權重為5。
這些都屬于軟體層面的負載均衡算法。
叢集容錯政策
failover cluster模式
預設,失敗自動切換,自動重試其他機器,常用于讀操作
failfast cluster模式
一次調用失敗就立即失敗,常用于寫操作
failsafe cluster模式
發生異常時忽略掉,常用于不重要的接口調用,如記錄日志
failbackc cluster模式
失敗了背景自動記錄請求,然後定時重發,适于寫消息隊列
forking cluster
并行調用多個provider,隻要一個成功就立即傳回
broadcacst cluster
逐個調用所有的provider
動态代理政策
預設使用javassist動态位元組碼生成,建立代理類
但是可以通過spi擴充機制配置自己的動态代理政策
參考