国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Nacos客戶端是如何實現實例獲取的負載均衡呢?

開發 前端
本篇文章追蹤Nacos客戶端源碼,分析了從實例列表中獲得其中一個實例的算法,也就是隨機權重負載均衡算法。整體業務邏輯比較簡單,從ServiceInfo中獲得實例列表,一路篩選,選中目標實例,然后根據它們的權重進行二次處理,數據結構封裝,最后基于Arrays#binarySearch提供的二分查找法來獲得對應的實例。

[[418899]]

前面我們講了Nacos客戶端如何獲取實例列表,如何進行緩存處理,以及如何訂閱實例列表的變更。在獲取到一個實例列表之后,你是否想過一個問題:如果實例列表有100個實例,Nacos客戶端是如何從中選擇一個呢?

這篇文章,就帶大家從源碼層面分析一下,Nacos客戶端采用了如何的算法來從實例列表中獲取一個實例進行請求的。也可以稱作是Nacos客戶端的負載均衡算法。

單個實例獲取

NamingService不僅提供了獲取實例列表的方法,也提供了獲取單個實例的方法,比如:

  1. Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe) 
  2.         throws NacosException; 

該方法會根據預定義的負載算法,從實例列表中獲得一個健康的實例。其他重載的方法功能類似,最終都會調用該方法,我們就以此方法為例來分析一下具體的算法。

具體實現代碼:

  1. @Override 
  2. public Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, 
  3.         boolean subscribe) throws NacosException { 
  4.     String clusterString = StringUtils.join(clusters, ","); 
  5.     if (subscribe) { 
  6.         // 獲取ServiceInfo 
  7.         ServiceInfo serviceInfo = serviceInfoHolder.getServiceInfo(serviceName, groupName, clusterString); 
  8.         if (null == serviceInfo) { 
  9.             serviceInfo = clientProxy.subscribe(serviceName, groupName, clusterString); 
  10.         } 
  11.         // 通過負載均衡算法獲得其中一個實例 
  12.         return Balancer.RandomByWeight.selectHost(serviceInfo); 
  13.     } else { 
  14.         // 獲取ServiceInfo 
  15.         ServiceInfo serviceInfo = clientProxy 
  16.                 .queryInstancesOfService(serviceName, groupName, clusterString, 0, false); 
  17.         // 通過負載均衡算法獲得其中一個實例 
  18.         return Balancer.RandomByWeight.selectHost(serviceInfo); 
  19.     } 

selectOneHealthyInstance方法邏輯很簡單,調用我們之前講到的方法獲取ServiceInfo對象,然后作為參數傳遞給負載均衡算法,由負載均衡算法計算出最終使用哪個實例(Instance)。

算法參數封裝

先跟蹤一下代碼實現,非核心業務邏輯,只簡單提一下。

上面的代碼可以看出調用的是Balancer內部類RandomByWeight的selectHost方法:

  1. public static Instance selectHost(ServiceInfo dom) { 
  2.     // ServiceInfo中獲去實例列表 
  3.     List<Instance> hosts = selectAll(dom); 
  4.     // ... 
  5.     return getHostByRandomWeight(hosts); 

selectHost方法核心邏輯是從ServiceInfo中獲取實例列表,然后調用getHostByRandomWeight方法:

  1. protected static Instance getHostByRandomWeight(List<Instance> hosts) { 
  2.     // ... 判斷邏輯 
  3.     // 重新組織數據格式 
  4.     List<Pair<Instance>> hostsWithWeight = new ArrayList<Pair<Instance>>(); 
  5.     for (Instance host : hosts) { 
  6.         if (host.isHealthy()) { 
  7.             hostsWithWeight.add(new Pair<Instance>(host, host.getWeight())); 
  8.         } 
  9.     } 
  10.     // 通過Chooser來實現隨機權重負載均衡算法 
  11.     Chooser<String, Instance> vipChooser = new Chooser<String, Instance>("www.taobao.com"); 
  12.     vipChooser.refresh(hostsWithWeight); 
  13.     return vipChooser.randomWithWeight(); 

getHostByRandomWeight前半部分是將Instance列表及其中的權重數據進行轉換,封裝成一個Pair,也就是建立成對的關系。在此過程中只使用了健康的節點。

真正的算法實現則是通過Chooser類來實現的,看名字基本上知道實現的策略是基于權重的隨機算法。

負載均衡算法實現

所有的負載均衡算法實現均位于Chooser類中,Chooser類的提供了兩個方法refresh和randomWithWeight。

refresh方法用于篩選數據、檢查數據合法性和建立算法所需數據模型。

randomWithWeight方法基于前面的數據來進行隨機算法處理。

先看refresh方法:

  1. public void refresh(List<Pair<T>> itemsWithWeight) { 
  2.     Ref<T> newRef = new Ref<T>(itemsWithWeight); 
  3.     // 準備數據,檢查數據 
  4.     newRef.refresh(); 
  5.     // 上面數據刷新之后,這里重新初始化一個GenericPoller 
  6.     newRef.poller = this.ref.poller.refresh(newRef.items); 
  7.     this.ref = newRef; 

基本步驟:

  • 創建Ref類,該類為Chooser的內部類;
  • 調用Ref的refresh方法,用于準備數據、檢查數據等;
  • 數據篩選完成,調用poller#refresh方法,本質上就是創建一個GenericPoller對象;
  • 成員變量重新賦值;

這里重點看Ref#refresh方法:

  1. /** 
  2.  * 獲取參與計算的實例列表、計算遞增數組數總和并進行檢查 
  3.  */ 
  4. public void refresh() { 
  5.     // 實例權重總和 
  6.     Double originWeightSum = (double) 0; 
  7.      
  8.     // 所有健康權重求和 
  9.     for (Pair<T> item : itemsWithWeight) { 
  10.          
  11.         double weight = item.weight(); 
  12.         //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest 
  13.         // 權重小于等于0則不參與計算 
  14.         if (weight <= 0) { 
  15.             continue
  16.         } 
  17.         // 有效實例放入列表 
  18.         items.add(item.item()); 
  19.         // 如果值無限大 
  20.         if (Double.isInfinite(weight)) { 
  21.             weight = 10000.0D; 
  22.         } 
  23.         // 如果值為非數字 
  24.         if (Double.isNaN(weight)) { 
  25.             weight = 1.0D; 
  26.         } 
  27.         // 權重值累加 
  28.         originWeightSum += weight; 
  29.     } 
  30.      
  31.     double[] exactWeights = new double[items.size()]; 
  32.     int index = 0; 
  33.     // 計算每個節點權重占比,放入數組 
  34.     for (Pair<T> item : itemsWithWeight) { 
  35.         double singleWeight = item.weight(); 
  36.         //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest 
  37.         if (singleWeight <= 0) { 
  38.             continue
  39.         } 
  40.         // 計算每個節點權重占比 
  41.         exactWeights[index++] = singleWeight / originWeightSum; 
  42.     } 
  43.      
  44.     // 初始化遞增數組 
  45.     weights = new double[items.size()]; 
  46.     double randomRange = 0D; 
  47.     for (int i = 0; i < index; i++) { 
  48.         // 遞增數組第i項值為items前i個值總和 
  49.         weights[i] = randomRange + exactWeights[i]; 
  50.         randomRange += exactWeights[i]; 
  51.     } 
  52.      
  53.     double doublePrecisionDelta = 0.0001; 
  54.     // index遍歷完則返回; 
  55.     // 或weights最后一位值與1相比,誤差小于0.0001,則返回 
  56.     if (index == 0 || (Math.abs(weights[index - 1] - 1) < doublePrecisionDelta)) { 
  57.         return
  58.     } 
  59.     throw new IllegalStateException( 
  60.             "Cumulative Weight calculate wrong , the sum of probabilities does not equals 1."); 

可結合上面代碼中的注釋來理解,核心步驟包括以下:

  • 遍歷itemsWithWeight,計算權重總和數據;非健康節點會被剔除掉;
  • 計算每個節點的權重值在總權重值中的占比,并存儲在exactWeights數組當中;
  • 將exactWeights數組當中值進行數據重構,形成一個遞增數組weights(每個值都是exactWeights坐標值的總和),后面用于隨機算法;
  • 判斷是否循環完成或誤差在指定范圍內(0.0001),符合則返回。

所有數據準備完成,調用隨機算法方法randomWithWeight:

  1. public T randomWithWeight() { 
  2.     Ref<T> ref = this.ref; 
  3.     // 生成0-1之間的隨機數 
  4.     double random = ThreadLocalRandom.current().nextDouble(0, 1); 
  5.     // 采用二分法查找數組中指定值,如果不存在則返回(-(插入點) - 1),插入點即隨機數將要插入數組的位置,即第一個大于此鍵的元素索引。 
  6.     int index = Arrays.binarySearch(ref.weights, random); 
  7.     // 如果沒有查詢到(返回-1或"-插入點") 
  8.     if (index < 0) { 
  9.         index = -index - 1; 
  10.     } else { 
  11.         // 命中直接返回結果 
  12.         return ref.items.get(index); 
  13.     } 
  14.      
  15.     // 判斷坐標未越界 
  16.     if (index < ref.weights.length) { 
  17.         // 隨機數小于指定坐標的數值,則返回坐標值 
  18.         if (random < ref.weights[index]) { 
  19.             return ref.items.get(index); 
  20.         } 
  21.     } 
  22.      
  23.     // 此種情況不應該發生,但如果發生則返回最后一個位置的值 
  24.     /* This should never happen, but it ensures we will return a correct 
  25.      * object in case there is some floating point inequality problem 
  26.      * wrt the cumulative probabilities. */ 
  27.     return ref.items.get(ref.items.size() - 1); 

該方法的基本操作如下:

  • 生成一個0-1的隨機數;
  • 使用Arrays#binarySearch在數組中進行查找,也就是二分查找法。該方法會返回包含key的值,如果沒有則會返回”-1“或”-插入點“,插入點即隨機數將要插入數組的位置,即第一個大于此鍵的元素索引。
  • 如果命中則直接返回;如果未命中則對返回值取反減1,獲得index值;
  • 判斷index值,符合條件,則返回結果;

至此,關于Nacos客戶端實例獲取的負載均衡算法代碼層面追蹤完畢。

算法實例演示

下面用一個實例來演示一下,該算法中涉及的數據變化。為了數據美觀,這里采用4組數據,每組數據進來確保能被整除;

節點及權重數據(前面節點,后面權重)如下:

  1. 1 100 
  2. 2 25 
  3. 3 75 
  4. 4 200 

第一步,計算權重綜合:

  1. originWeightSum = 100 + 25 + 75 + 200 = 400 

第二步,計算每個節點權重比:

  1. exactWeights = {0.25, 0.0625, 0.1875, 0.5} 

第三步,計算遞增數組weights:

  1. weights = {0.25, 0.3125, 0.5, 1} 

第四步,生成0-1的隨機數:

  1. random = 0.3049980013493817 

第五步,調用Arrays#binarySearch從weights中搜索random:

  1. index = -2 

關于Arrays#binarySearch(double[] a, double key)方法這里再解釋一下,如果傳入的key恰好在數組中,比如1,則返回的index為3;如果key為上面的random值,則先找到插入點,取反,減一。

插入點即第一個大于此key的元素索引,那么上面第一個大于0.3049980013493817的值為0.3125,那么插入點值為1;

于是按照公式計算Arrays#binarySearch返回的index為:

  1. index = - ( 1 ) - 1 = -2 

第六步,也就是沒有恰好命中的情況:

  1. index = -( -2 ) - 1 = 1 

然后判斷index是否越界,很明顯 1 < 4,未越界,則返回坐標為1的值。

算法的核心

上面演示了算法,但這個算法真的能夠做到按權重負載嗎?我們來分析一下這個問題。

這個問題的重點不在random值,這個值基本上是隨機的,那么怎么保證權重大的節點獲得的機會更多呢?

這里先把遞增數組weights用另外一個形式來表示:

上面的算法可以看出,weights與exactWeights為size相同的數組,對于同一坐標(index),weights的值是exactWeights包含當前坐標及前面所有坐標值的和。

如果把weights理解成一條線,對應節點的值是線上的一個個點,體現在圖中便是(圖2到圖5)有色(灰色+橘黃色)部分。

而Arrays#binarySearch算法的插入點獲取的是第一個大于key(也就是random)的坐標,也就是說每個節點享有的隨機范圍不同,它們的范圍由當前點和前一個點的區間決定,而這個區間正好是權重比值。

權重比值大的節點,占有的區間就比較多,比如節點1占了1/4,節點4占了1/2。這樣,如果隨機數是均勻分布的,那么占有范圍比較大的節點更容易獲得青睞。也就達到了按照權重獲得被調用的機會了。

小結

本篇文章追蹤Nacos客戶端源碼,分析了從實例列表中獲得其中一個實例的算法,也就是隨機權重負載均衡算法。整體業務邏輯比較簡單,從ServiceInfo中獲得實例列表,一路篩選,選中目標實例,然后根據它們的權重進行二次處理,數據結構封裝,最后基于Arrays#binarySearch提供的二分查找法來獲得對應的實例。

 

而我們需要注意和學習的重點便是權重獲取算法的思想及具體實現,最終達到能夠在實踐中進行運用。

 

責任編輯:武曉燕 來源: 程序新視界
相關推薦

2010-04-21 12:57:33

RAC負載均衡配置

2021-04-30 08:19:32

SpringCloud客戶端負載Ribbo

2023-10-30 11:28:33

Kubernetes負載均衡

2011-08-17 10:10:59

2019-06-19 14:58:38

服務器負載均衡客戶端

2019-09-10 09:58:19

Dubbo負載均衡Hash

2010-12-21 11:03:15

獲取客戶端證書

2021-07-16 06:56:50

Nacos注冊源碼

2011-12-15 11:03:21

JavaNIO

2019-10-29 05:34:34

IPJava服務器

2021-06-22 15:06:13

Redis客戶端 Redis-clie

2023-11-15 13:50:07

服務端IP

2018-12-19 10:31:32

客戶端IP服務器

2025-04-15 10:00:00

Feign負載均衡微服務

2021-09-22 15:46:29

虛擬桌面瘦客戶端胖客戶端

2010-05-10 17:52:30

實現負載均衡

2025-08-28 08:26:47

2010-12-31 14:23:57

Exchange Se

2013-03-13 10:51:44

瘦客戶端VDI

2021-08-06 06:51:14

NacosRibbon服務
點贊
收藏

51CTO技術棧公眾號

亚洲一区二区三区在线| 嫩草嫩草嫩草嫩草| 亚洲跨种族黑人xxx| 波多野结衣在线aⅴ中文字幕不卡| 九色精品91| 玛雅亚洲电影| av在线官网| 亚洲精品少妇久久久久久| 中文字幕在线中文字幕日亚韩一区 | 国产精品最新在线观看| 久久精品国产精品亚洲| 精品国产污网站| 欧美在线观看视频在线| 亚洲欧美在线视频观看| 不卡的av中国片| 国产精品一区久久久久| 蜜桃一区二区三区四区| 亚洲作爱视频| 国产精品二区影院| 97精品中文字幕| 成人在线一区| 欧美肉体xxxx裸体137大胆| 国产精品久一| 1769国产精品视频| 亚洲精品在线播放| 国产极品模特精品一二| 精品女人视频| 超碰97久久| 精品成人影院| 色婷婷热久久| 亚洲人人精品| 国产精品美女久久久| 日韩和欧美一区二区| 久久午夜av| 成人午夜av影视| 中文字幕一区二区三区蜜月| 综合色中文字幕| 亚洲精选视频免费看| 日本电影亚洲天堂一区| 欧美变态tickling挠脚心| 亚洲天堂久久av| 国内久久久精品| 国产免费一区二区三区香蕉精| 国产精品一区二区在线观看| 超碰97免费观看| youjizzxxxx18| 国产区av在线| a级在线观看| 亚洲三区欧美一区国产二区| 日本欧美视频| 麻豆精品一二三| 一区二区在线观看视频在线观看| 日本精品视频一区二区三区| 亚洲精品中文字幕女同| 欧美噜噜久久久xxx| 91蜜桃网站免费观看| 丰满女人性猛交| 宅男午夜在线| 日本在线播放一二三区| 激情av综合| 直接在线观看的三级网址| 91超碰在线播放| 在线日韩视频| 欧美美女激情18p| 久久综合福利| 日本不卡在线| 久久97超碰色| 一区二区三区精品99久久| 日本人体一区二区| 亚洲a∨精品一区二区三区导航| 国产一区二区三区在线观看免费视频| 亚洲国产精品字幕| 日本欧美黄色片| 蜜臀av免费一区二区三区| 欧美日韩成人一区| 99久久一区三区四区免费| jzzjzzjzz亚洲成熟少妇| 韩日精品视频| 3d动漫精品啪啪一区二区竹菊| 91视频九色网站| 中文字幕视频在线| 天天揉久久久久亚洲精品| 国产人妖乱国产精品人妖| 国内精品400部情侣激情| 91九色porny视频| 欧美一站二站| 欧美乱妇15p| 国产91porn| 精品精品国产三级a∨在线| 日韩一区二区电影网| 国产精品久久在线观看| 国产精品乱码| 蜜桃视频在线网站| 国产午夜精品一区二区三区四区| 国产91精品久久久久| 狠狠狠综合7777久夜色撩人 | 久久精品亚洲精品国产欧美kt∨ | 欧美一二三不卡| 伊人久久综合网另类网站| 国产精品久久久久久福利一牛影视 | 欧美日韩国产另类不卡| 精品国产乱码久久久久久88av| 污污的网站在线免费观看| 国v精品久久久网| 久久亚洲精品成人| 色鬼7777久久| 久久99最新地址| 欧美最猛黑人xxxx黑人猛叫黄| 免费黄色电影在线观看| 国产欧美精品一区| 日本精品一区| 97视频精品| 992tv成人免费视频| 台湾av在线二三区观看| 自拍偷拍欧美| 91国产免费观看| 国产欧美日韩在线播放| 欧美黄色三级| 一色桃子久久精品亚洲| 粉嫩高清一区二区三区精品视频| 网址你懂得在线观看| 日韩成人精品在线| 国产福利电影网| 久久久99精品免费观看不卡| 鲁一鲁一鲁一鲁一av| 韩日精品一区| 欧美激情一区二区三区全黄| 国模gogo一区二区大胆私拍| 先锋影音资源999| 亚洲美女久久| 欧美性猛交xxxx免费看漫画 | 欧美一区二区大胆人体摄影专业网站| 美女视频黄a视频全免费观看| 在线观看日韩av电影| 亚洲一级免费视频| 91国产精品视频在线| 成人欧美一区| 粉嫩一区二区三区在线看 | 97免费中文视频在线观看| 亚洲人av在线| 国产精品一区二区你懂的| 午夜免费在线观看精品视频| 超碰在线免费公开| 中文字幕日韩av资源站| 激情欧美一区二区三区中文字幕| 亚洲欧美一级| 91国模大尺度私拍在线视频| 日韩av在线第一页| 一区在线免费| 欧美中文在线免费| 亚洲精品国产嫩草在线观看| 欧美日韩国产三级| 99re99| 国产精品一区二区久久精品爱涩 | 欧美日韩免费做爰大片| 91看片淫黄大片一级在线观看| 97超碰人人看人人| 加勒比视频一区| 亚洲男子天堂网| 国产一区久久精品| 色综合欧美在线| 日本最黄视频| 国产亚洲欧美日韩日本| 女人帮男人橹视频播放| 久久99精品国产麻豆婷婷洗澡| 狠狠色狠狠色综合人人| av资源网站在线观看| 五月婷婷激情综合| xx免费视频| 亚洲欧洲精品天堂一级| 成人精品视频在线播放| 日本欧美一区二区三区| 国产伦视频一区二区三区| 亚洲国产精品成人| 国产精品情侣自拍| 98精品视频| 成人免费91在线看| 综合激情一区| 国产精品久久久久久久久久直播 | 在线播放豆国产99亚洲| 日韩精品不卡一区二区| 日韩成人18| 成人直播大秀| 久久久久国产精品一区三寸| 国产精品白丝jk黑袜喷水| 欧美激情一区在线| 在线免费亚洲电影| 亚洲香蕉成视频在线观看| 欧美乱妇40p| 成人资源av| 成人在线观看www| 免费av播放| 97超碰在线免费| 欧美电影免费网站| 丝袜美腿一区二区三区| 亚洲欧美一区二区三区| 免费成人av| 蜜桃av一区二区三区| 一区二区在线观看免费| 日韩成人在线视频网站|