日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Dubbo 源码分析 - 集群容错之 LoadBalance

發布時間:2025/3/21 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Dubbo 源码分析 - 集群容错之 LoadBalance 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.簡介

LoadBalance 中文意思為負載均衡,它的職責是將網絡請求,或者其他形式的負載“均攤”到不同的機器上。避免集群中部分服務器壓力過大,而另一些服務器比較空閑的情況。通過負載均衡,可以讓每臺服務器獲取到適合自己處理能力的負載。在為高負載的服務器分流的同時,還可以避免資源浪費,一舉兩得。負載均衡可分為軟件負載均衡和硬件負載均衡。在我們日常開發中,一般很難接觸到硬件負載均衡。但軟件負載均衡還是能夠接觸到一些的,比如 Nginx。在 Dubbo 中,也有負載均衡的概念和相應的實現。Dubbo 需要對服務消費者的調用請求進行分配,避免少數服務提供者負載過大。服務提供者負載過大,會導致部分服務調用超時。因此將負載均衡到每個服務提供者上,是非常必要的。Dubbo 提供了4種負載均衡實現,分別是基于權重隨機算法的 RandomLoadBalance、基于最少活躍調用數算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加權輪詢算法的 RoundRobinLoadBalance。這幾個負載均衡算法代碼不是很長,但是想看懂也不是很容易,需要大家對這幾個算法的原理有一定了解才行。如果不是很了解,也沒不用太擔心。我會在分析每個算法的源碼之前,對算法原理進行簡單的講解,幫助大家建立初步的印象。

我在寫 Dubbo 源碼分析系列文章之初,當時 Dubbo 最新的版本為 2.6.4。近期,Dubbo 2.6.5 發布了,其中就有對負載均衡部分代碼修改。因此我在分析完 2.6.4 版本后的源碼后,會另外分析 2.6.5 更新的部分。本篇文章內容非常之豐富,需要大家耐心閱讀。好了,其他的就不多說了,進入正題吧。

?2.源碼分析

在 Dubbo 中,所有負載均衡實現類均繼承自 AbstractLoadBalance,該類實現了 LoadBalance 接口方法,并封裝了一些公共的邏輯。所以在分析負載均衡實現之前,先來看一下 AbstractLoadBalance 的邏輯。首先來看一下負載均衡的入口方法 select,如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {if (invokers == null || invokers.isEmpty())return null;// 如果 invokers 列表中僅有一個 Invoker,直接返回即可,無需進行負載均衡if (invokers.size() == 1)return invokers.get(0);// 調用 doSelect 方法進行負載均衡,該方法為抽象方法,由子類實現return doSelect(invokers, url, invocation); }protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

select 方法的邏輯比較簡單,首先會檢測 invokers 集合的合法性,然后再檢測 invokers 集合元素數量。如果只包含一個 Invoker,直接返回該 Inovker 即可。如果包含多個 Invoker,此時需要通過負載均衡算法選擇一個 Invoker。具體的負載均衡算法由子類實現,接下來章節會對這些子類進行詳細分析。

AbstractLoadBalance 除了實現了 LoadBalance 接口方法,還封裝了一些公共邏輯 —— 服務提供者權重計算邏輯。具體實現如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 protected int getWeight(Invoker<?> invoker, Invocation invocation) {// 從 url 中獲取 weight 配置值int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);if (weight > 0) {// 獲取服務提供者啟動時間戳long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L);if (timestamp > 0L) {// 計算服務提供者運行時長int uptime = (int) (System.currentTimeMillis() - timestamp);// 獲取服務預熱時間,默認為10分鐘int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP);// 如果服務運行時間小于預熱時間,則重新計算服務權重,即降權if (uptime > 0 && uptime < warmup) {// 重新計算服務權重weight = calculateWarmupWeight(uptime, warmup, weight);}}}return weight; }static int calculateWarmupWeight(int uptime, int warmup, int weight) {// 計算權重,下面代碼邏輯上形似于 (uptime / warmup) * weight。// 隨著服務運行時間 uptime 增大,權重計算值 ww 會慢慢接近配置值 weightint ww = (int) ((float) uptime / ((float) warmup / (float) weight));return ww < 1 ? 1 : (ww > weight ? weight : ww); }

上面是權重的計算過程,該過程主要用于保證當服務運行時長小于服務預熱時間時,對服務進行降權,避免讓服務在啟動之初就處于高負載狀態。服務預熱是一個優化手段,與此類似的還有 JVM 預熱。主要目的是讓服務啟動后“低功率”運行一段時間,使其效率慢慢提升至最佳狀態。關于預熱方面的更多知識,大家感興趣可以自己搜索一下。

關于 AbstractLoadBalance 就先分析到這,接下來分析各個實現類的代碼。首先,我們從 Dubbo 缺省的實現類 RandomLoadBalance 看起。

?2.1 RandomLoadBalance

RandomLoadBalance 是加權隨機算法的具體實現,它的算法思想很簡單。假設我們有一組服務器 servers = [A, B, C],他們對應的權重為 weights = [5, 3, 2],權重總和為10。現在把這些權重值平鋪在一維坐標值上,[0, 5) 區間屬于服務器 A,[5, 8) 區間屬于服務器 B,[8, 10) 區間屬于服務器 C。接下來通過隨機數生成器生成一個范圍在 [0, 10) 之間的隨機數,然后計算這個隨機數會落到哪個區間上。比如數字3會落到服務器 A 對應的區間上,此時返回服務器 A 即可。權重越大的機器,在坐標軸上對應的區間范圍就越大,因此隨機數生成器生成的數字就會有更大的概率落到此區間內。只要隨機數生成器產生的隨機數分布性很好,在經過多次選擇后,每個服務器被選中的次數比例接近其權重比例。比如,經過一萬次選擇后,服務器 A 被選中的次數大約為5000次,服務器 B 被選中的次數約為3000次,服務器 C 被選中的次數約為2000次。

以上就是 RandomLoadBalance 背后的算法思想,比較簡單,不多說了,下面開始分析源碼。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class RandomLoadBalance extends AbstractLoadBalance {public static final String NAME = "random";private final Random random = new Random();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size();int totalWeight = 0;boolean sameWeight = true;// 下面這個循環有兩個作用,第一是計算總權重 totalWeight,// 第二是檢測每個服務提供者的權重是否相同,若不相同,則將 sameWeight 置為 falsefor (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// 累加權重totalWeight += weight;// 檢測當前服務提供者的權重與上一個服務提供者的權重是否相同,// 不相同的話,則將 sameWeight 置為 false。if (sameWeight && i > 0&& weight != getWeight(invokers.get(i - 1), invocation)) {sameWeight = false;}}// 下面的 if 分支主要用于獲取隨機數,并計算隨機數落在哪個區間上if (totalWeight > 0 && !sameWeight) {// 隨機獲取一個 [0, totalWeight) 之間的數字int offset = random.nextInt(totalWeight);// 循環讓 offset 數減去服務提供者權重值,當 offset 小于0時,返回相應的 Invoker。// 還是用上面的例子進行說明,servers = [A, B, C],weights = [5, 3, 2],offset = 7。// 第一次循環,offset - 5 = 2 > 0,說明 offset 肯定不會落在服務器 A 對應的區間上。// 第二次循環,offset - 3 = -1 < 0,表明 offset 落在服務器 B 對應的區間上for (int i = 0; i < length; i++) {// 讓隨機值 offset 減去權重值offset -= getWeight(invokers.get(i), invocation);if (offset < 0) {// 返回相應的 Invokerreturn invokers.get(i);}}}// 如果所有服務提供者權重值相同,此時直接隨機返回一個即可return invokers.get(random.nextInt(length));} }

RandomLoadBalance 的算法思想比較簡單,在經過多次請求后,能夠將調用請求按照權重值進行“均勻”分配。當然 RandomLoadBalance 也存在一定的缺點,當調用次數比較少時,Random 產生的隨機數可能會比較集中,此時多數請求會落到同一臺服務器上。這個缺點并不是很嚴重,多數情況下可以忽略。RandomLoadBalance 是一個簡單,高效的負載均衡實現,因此 Dubbo 選擇它作為缺省實現。

關于 RandomLoadBalance 就先到這了,接下來分析 LeastActiveLoadBalance。

?2.2 LeastActiveLoadBalance

LeastActiveLoadBalance 翻譯過來是最小活躍數負載均衡,所謂的最小活躍數可理解為最少連接數。即服務提供者目前正在處理的請求數(一個請求對應一條連接)最少,表明該服務提供者效率高,單位時間內可處理更多的請求。此時應優先將請求分配給該服務提供者。在具體實現中,每個服務提供者對應一個活躍數 active。初始情況下,所有服務提供者活躍數均為0。每收到一個請求,活躍數加1,完成請求后則將活躍數減1。在服務運行一段時間后,性能好的服務提供者處理請求的速度更快,因此活躍數下降的也越快。此時這樣的服務提供者能夠優先獲取到新的服務請求,這就是最小活躍數負載均衡算法的基本思想。除了最小活躍數,LeastActiveLoadBalance 在實現上還引入了權重值。所以準確的來說,LeastActiveLoadBalance 是基于加權最小活躍數算法實現的。舉個例子說明一下,在一個服務提供者集群中,有兩個性能優異的服務提供者。某一時刻它們的活躍數相同,此時 Dubbo 會根據它們的權重去分配請求,權重越大,獲取到新請求的可能性就越大。如果兩個服務提供者權重相同,此時隨機選擇一個即可。關于 LeastActiveLoadBalance 的背景知識就先介紹到這里,下面開始分析源碼。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public class LeastActiveLoadBalance extends AbstractLoadBalance {public static final String NAME = "leastactive";private final Random random = new Random();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size();// 最小的活躍數int leastActive = -1;// 具有相同“最小活躍數”的服務者提供者(以下用 Invoker 代稱)數量int leastCount = 0; // leastIndexs 用于記錄具有相同“最小活躍數”的 Invoker 在 invokers 列表中的下標信息int[] leastIndexs = new int[length];int totalWeight = 0;// 第一個最小活躍數的 Invoker 權重值,用于與其他具有相同最小活躍數的 Invoker 的權重進行對比,// 以檢測是否所有具有相同最小活躍數的 Invoker 的權重均相等int firstWeight = 0;boolean sameWeight = true;// 遍歷 invokers 列表for (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);// 獲取 Invoker 對應的活躍數int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// 獲取權重 - ??int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);// 發現更小的活躍數,重新開始if (leastActive == -1 || active < leastActive) {// 使用當前活躍數 active 更新最小活躍數 leastActiveleastActive = active;// 更新 leastCount 為 1leastCount = 1;// 記錄當前下標值到 leastIndexs 中leastIndexs[0] = i;totalWeight = weight;firstWeight = weight;sameWeight = true;// 當前 Invoker 的活躍數 active 與最小活躍數 leastActive 相同 } else if (active == leastActive) {// 在 leastIndexs 中記錄下當前 Invoker 在 invokers 集合中的下標leastIndexs[leastCount++] = i;// 累加權重totalWeight += weight;// 檢測當前 Invoker 的權重與 firstWeight 是否相等,// 不相等則將 sameWeight 置為 falseif (sameWeight && i > 0&& weight != firstWeight) {sameWeight = false;}}}// 當只有一個 Invoker 具有最小活躍數,此時直接返回該 Invoker 即可if (leastCount == 1) {return invokers.get(leastIndexs[0]);}// 有多個 Invoker 具有相同的最小活躍數,但他們的權重不同if (!sameWeight && totalWeight > 0) {// 隨機獲取一個 [0, totalWeight) 之間的數字int offsetWeight = random.nextInt(totalWeight);// 循環讓隨機數減去具有最小活躍數的 Invoker 的權重值,// 當 offset 小于等于0時,返回相應的 Invokerfor (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexs[i];// 獲取權重值,并讓隨機數減去權重值 - ??offsetWeight -= getWeight(invokers.get(leastIndex), invocation);if (offsetWeight <= 0)return invokers.get(leastIndex);}}// 如果權重相同或權重為0時,隨機返回一個 Invokerreturn invokers.get(leastIndexs[random.nextInt(leastCount)]);} }

如上,為了幫助大家理解代碼,我在上面的代碼中寫了大量的注釋。下面簡單總結一下以上代碼所做的事情,如下:

  • 遍歷 invokers 列表,尋找活躍數最小的 Invoker
  • 如果有多個 Invoker 具有相同的最小活躍數,此時記錄下這些 Invoker 在 invokers 集合中的下標,以及累加它們的權重,比較它們之間的權重值是否相等
  • 如果只有一個 Invoker 具有最小的活躍數,此時直接返回該 Invoker 即可
  • 如果有多個 Invoker 具有最小活躍數,且它們的權重不相等,此時處理方式和 RandomLoadBalance 一致
  • 如果有多個 Invoker 具有最小活躍數,但它們的權重相等,此時隨機返回一個即可
  • 以上就是 LeastActiveLoadBalance 大致的實現邏輯,大家在閱讀的源碼的過程中要注意區分活躍數與權重這兩個概念,不要混為一談。

    以上分析是基于 Dubbo 2.6.4 版本進行了,由于近期 Dubbo 2.6.5 發布了,對負載均衡部分的代碼進行了一些更新。這其中就包含了本節分析的 LeastActiveLoadBalance,所以下面簡單說明一下 Dubbo 2.6.5 對 LeastActiveLoadBalance 進行了怎樣的修改。回到上面的源碼中,我在上面的代碼中標注了兩個黃色的五角星??。兩處標記對應的代碼分別如下:

    1 int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
    1 offsetWeight -= getWeight(invokers.get(leastIndex), invocation);

    問題出在服務預熱階段,第一行代碼直接從 url 中去權重值,未被降權過。第二行代碼獲取到的是經過降權后的權重。第一行代碼獲取到的權重值最終會被累加到權重總和 totalWeight 中,這個時候會導致一個問題。offsetWeight 是一個在 [0, totalWeight) 范圍內的隨機數,而它所減去的是經過降權的權重。很有可能在經過 leastCount 次運算后,offsetWeight 仍然是大于0的,導致無法選中 Invoker。這個問題對應的 issue 為?#904,在 pull request?#2172?中被修復。具體的修復邏輯是將標注一處的代碼修改為:

    1 2 // afterWarmup 等價于上面的 weight 變量,這樣命名是為了強調該變量經過 warmup 降權處理了 int afterWarmup = getWeight(invoker, invocation);

    另外,2.6.4 版本中的 LeastActiveLoadBalance 還要一個缺陷,即當一組 Invoker 具有相同的最小活躍數,且其中一個 Invoker 的權重值為1,此時這個 Invoker 無法被選中。缺陷代碼如下:

    1 2 3 4 5 6 7 int offsetWeight = random.nextInt(totalWeight); for (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexs[i];offsetWeight -= getWeight(invokers.get(leastIndex), invocation);if (offsetWeight <= 0) // ?return invokers.get(leastIndex); }

    問題就出在了offsetWeight <= 0上,舉例說明,假設有一組 Invoker 的權重為 5、2、1,offsetWeight 最大值為 7。假設 offsetWeight = 7,你會發現,當 for 循環進行第二次遍歷后 offsetWeight = 7 - 5 - 2 = 0,提前返回了。此時,權重為1的 Invoker 就沒有機會被選中。這個修改起來也不難,可以將?offsetWeight < 0,不過 Dubbo 的是將offsetWeight + 1,也就是:

    1 int offsetWeight = random.nextInt(totalWeight) + 1;

    兩種改動都行,不過我認為覺得第一種方式更好一點,可與 RandomLoadBalance 邏輯保持一致。這里+1有點突兀,大家讀到這里要特地思考一下為什么要+1。

    以上就是 Dubob 2.6.5 對 LeastActiveLoadBalance 的更新,不是很難理解,就不多說了。接下來分析基于一致性 hash 思想的 ConsistentHashLoadBalance。

    ?2.3 ConsistentHashLoadBalance

    一致性 hash 算法由麻省理工學院的 Karger 及其合作者于1997年提供出的,算法提出之初是用于大規模緩存系統的負載均衡。它的工作過程是這樣的,首先根據 ip 獲取其他的信息為緩存節點生成一個 hash,并將這個 hash 投射到 [0, 232?- 1] 的圓環上。當有查詢或寫入請求時,則為緩存項的 key 生成一個 hash 值。然后查找第一個大于或等于該 hash 值的緩存節點,并到這個節點中查詢或寫入緩存項。如果當前節點掛了,則在下一次查詢或寫入緩存時,為緩存項查找另一個大于其 hash 值的緩存節點即可。大致效果如下,每個緩存節點在圓環上占據一個位置。如果緩存項的 key 的 hash 值小于緩存節點 hash 值,則到該緩存節點中存儲或讀取緩存項。比如下面綠色點對應的緩存項存儲到 cache-2 節點中。由于 cache-3 掛了,原本應該存到該節點中的緩存想最終會存儲到 cache-4 節點中。

    關于一致性 hash 算法,我這里只做掃盲。具體的細節不討論,大家請自行補充相關的背景知識。下面來看看一致性 hash 在 Dubbo 中的應用。我們把上圖的緩存節點替換成 Dubbo 的服務提供者,于是得到了下圖:

    這里相同顏色的節點均屬于同一個服務提供者,比如 Invoker1-1,Invoker1-2,……, Invoker1-160。這樣做的目的是通過引入虛擬節點,讓 Invoker 在圓環上分散開來,避免數據傾斜問題。所謂數據傾斜是指,由于節點不夠分散,導致大量請求落到了同一個節點上,而其他節點只會接收到了少量的請求。比如:

    如上,由于 Invoker-1 和 Invoker-2 在圓環上分布不均,導致系統中75%的請求都會落到 Invoker-1 上,只有 25% 的請求會落到 Invoker-2 上。解決這個問題辦法是引入虛擬節點,通過虛擬節點均衡各個節點的請求量。

    到這里背景知識就普及完了,接下來開始分析源碼。我們先從 ConsistentHashLoadBalance 的 doSelect 方法開始看起,如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class ConsistentHashLoadBalance extends AbstractLoadBalance {private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {String methodName = RpcUtils.getMethodName(invocation);String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;// 獲取 invokers 原始的 hashcodeint identityHashCode = System.identityHashCode(invokers);ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);// 如果 invokers 是一個新的 List 對象,意味著服務提供者數量發生了變化,可能新增也可能減少了。// 此時 selector.identityHashCode != identityHashCode 條件成立if (selector == null || selector.identityHashCode != identityHashCode) {// 創建新的 ConsistentHashSelectorselectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));selector = (ConsistentHashSelector<T>) selectors.get(key);}// 調用 ConsistentHashSelector 的 select 方法選擇 Invokerreturn selector.select(invocation);}private static final class ConsistentHashSelector<T> {...} }

    如上,doSelect 方法主要做了一些前置工作,比如檢測 invokers 列表是不是變動過,以及創建 ConsistentHashSelector。這些工作做完后,接下來開始調用 select 方法執行負載均衡邏輯。在分析 select 方法之前,我們先來看一下一致性 hash 選擇器 ConsistentHashSelector 的初始化過程,如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private static final class ConsistentHashSelector<T> {// 使用 TreeMap 存儲 Invoker 虛擬節點private final TreeMap<Long, Invoker<T>> virtualInvokers;private final int replicaNumber;private final int identityHashCode;private final int[] argumentIndex;ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {this.virtualInvokers = new TreeMap<Long, Invoker<T>>();this.identityHashCode = identityHashCode;URL url = invokers.get(0).getUrl();// 獲取虛擬節點數,默認為160this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);// 獲取參與 hash 計算的參數下標值,默認對第一個參數進行 hash 運算String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));argumentIndex = new int[index.length];for (int i = 0; i < index.length; i++) {argumentIndex[i] = Integer.parseInt(index[i]);}for (Invoker<T> invoker : invokers) {String address = invoker.getUrl().getAddress();for (int i = 0; i < replicaNumber / 4; i++) {// 對 address + i 進行 md5 運算,得到一個長度為16的字節數組byte[] digest = md5(address + i);// 對 digest 部分字節進行4次 hash 運算,得到四個不同的 long 型正整數for (int h = 0; h < 4; h++) {// h = 0 時,取 digest 中下標為 0 ~ 3 的4個字節進行位運算// h = 1 時,取 digest 中下標為 4 ~ 7 的4個字節進行位運算// h = 2, h = 3 時過程同上long m = hash(digest, h);// 將 hash 到 invoker 的映射關系存儲到 virtualInvokers 中,// virtualInvokers 中的元素要有序,因此選用 TreeMap 作為存儲結構virtualInvokers.put(m, invoker);}}}} }

    ConsistentHashSelector 進行了一些列的初始化方法,比如從配置中獲取虛擬節點數以及參與 hash 計算的參數下標,默認情況下只使用第一個參數進行 hash。需要特別說明的是,ConsistentHashLoadBalance 的負載均衡邏輯只受參數值影響,具有相同參數值的請求將會被分配給同一個服務提供者。ConsistentHashLoadBalance 不 care 權重,因此使用時需要注意一下。

    在獲取虛擬節點數和參數下標配置后,接下來要做到事情是計算虛擬節點 hash 值,并將虛擬節點存儲到 TreeMap 中。到此,ConsistentHashSelector 初始化工作就完成了。接下來,我們再來看看 select 方法的邏輯。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public Invoker<T> select(Invocation invocation) {// 將參數轉為 keyString key = toKey(invocation.getArguments());// 對參數 key 進行 md5 運算byte[] digest = md5(key);// 取 digest 數組的前四個字節進行 hash 運算,再將 hash 值傳給 selectForKey 方法,// 尋找合適的 Invokerreturn selectForKey(hash(digest, 0)); }private Invoker<T> selectForKey(long hash) {// 到 TreeMap 中查找第一個節點值大于或等于當前 hash 的 InvokerMap.Entry<Long, Invoker<T>> entry = virtualInvokers.tailMap(hash, true).firstEntry();// 如果 hash 大于 Invoker 在圓環上最大的位置,此時 entry = null,// 需要將 TreeMap 的頭結點賦值給 entryif (entry == null) {entry = virtualInvokers.firstEntry();}// 返回 Invokerreturn entry.getValue(); }

    如上,選擇的過程比較簡單了。首先是對參數進行 md5 以及 hash 運算,得到一個 hash 值。然后再拿這個值到 TreeMap 中查找目標 Invoker 即可。

    到此關于 ConsistentHashLoadBalance 就分析完了。在閱讀 ConsistentHashLoadBalance 之前,大家一定要先補充背景知識。否者即使這里只有一百多行代碼,也很難看懂。好了,本節先分析到這。

    ?2.4 RoundRobinLoadBalance

    本節,我們來看一下 Dubbo 中的加權輪詢負載均衡的實現 RoundRobinLoadBalance。在詳細分析源碼前,我們還是先來了解一下什么是加權輪詢。這里從最簡單的輪詢開始講起,所謂輪詢就是將請求輪流分配給一組服務器。舉個例子,我們有三臺服務器 A、B、C。我們將第一個請求分配給服務器 A,第二個請求分配給服務器 B,第三個請求分配給服務器 C,第四個請求再次分配給服務器 A。這個過程就叫做輪詢。輪詢是一種無狀態負載均衡算法,實現簡單,適用于每臺服務器性能相近的場景下。顯然,現實情況下,我們并不能保證每臺服務器性能均相近。如果我們將等量的請求分配給性能較差的服務器,這顯然是不合理的。因此,這個時候我們需要加權輪詢算法,對輪詢過程進行干預,使得性能好的服務器可以得到更多的請求,性能差的得到的少一些。每臺服務器能夠得到的請求數比例,接近或等于他們的權重比。比如服務器 A、B、C 權重比為 5:2:1。那么在8次請求中,服務器 A 將獲取到其中的5次請求,服務器 B 獲取到其中的2次請求,服務器 C 則獲取到其中的1次請求。

    以上就是加權輪詢的算法思想,搞懂了這個思想,接下來我們就可以分析源碼了。我們先來看一下 2.6.4 版本的 RoundRobinLoadBalance。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = "roundrobin";private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// key = 全限定類名 + "." + 方法名,比如 com.xxx.DemoService.sayHelloString key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();int length = invokers.size();// 最大權重int maxWeight = 0;// 最小權重int minWeight = Integer.MAX_VALUE;final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Invoker<T>, IntegerWrapper>();// 權重總和int weightSum = 0;// 下面這個循環主要用于查找最大和最小權重,計算權重總和等for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// 獲取最大和最小權重maxWeight = Math.max(maxWeight, weight);minWeight = Math.min(minWeight, weight);if (weight > 0) {// 將 weight 封裝到 IntegerWrapper 中invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));// 累加權重weightSum += weight;}}// 查找 key 對應的對應 AtomicPositiveInteger 實例,為空則創建。// 這里可以把 AtomicPositiveInteger 看成一個黑盒,大家只要知道// AtomicPositiveInteger 用于記錄服務的調用編號即可。至于細節,// 大家如果感興趣,可以自行分析AtomicPositiveInteger sequence = sequences.get(key);if (sequence == null) {sequences.putIfAbsent(key, new AtomicPositiveInteger());sequence = sequences.get(key);}// 獲取當前的調用編號int currentSequence = sequence.getAndIncrement();// 如果 最小權重 < 最大權重,表明服務提供者之間的權重是不相等的if (maxWeight > 0 && minWeight < maxWeight) {// 使用調用編號對權重總和進行取余操作int mod = currentSequence % weightSum;// 進行 maxWeight 次遍歷for (int i = 0; i < maxWeight; i++) {// 遍歷 invokerToWeightMapfor (Map.Entry<Invoker<T>, IntegerWrapper> each : invokerToWeightMap.entrySet()) {// 獲取 Invokerfinal Invoker<T> k = each.getKey();// 獲取權重包裝類 IntegerWrapperfinal IntegerWrapper v = each.getValue();// 如果 mod = 0,且權重大于0,此時返回相應的 Invokerif (mod == 0 && v.getValue() > 0) {return k;}// mod != 0,且權重大于0,此時對權重和 mod 分別進行自減操作if (v.getValue() > 0) {v.decrement();mod--;}}}}// 服務提供者之間的權重相等,此時通過輪詢選擇 Invokerreturn invokers.get(currentSequence % length);}// IntegerWrapper 是一個 int 包裝類,主要包含了一個自減方法。// 與 Integer 不同,Integer 是不可變類,而 IntegerWrapper 是可變類private static final class IntegerWrapper {private int value;public void decrement() {this.value--;}// 省略部分代碼} }

    如上,RoundRobinLoadBalance 的每行代碼都不是很難理解,但是將它們組合到一起之后,好像就看不懂了。這里對上面代碼的主要邏輯進行總結,如下:

  • 找到最大權重值,并計算出權重和
  • 使用調用編號對權重總和進行取余操作,得到 mod
  • 檢測 mod 的值是否等于0,且 Invoker 權重是否大于0,如果兩個條件均滿足,則返回該 Invoker
  • 如果上面條件不滿足,且 Invoker 權重大于0,此時對 mod 和權重進行遞減
  • 再次循環,重復步驟3、4
  • 以上過程對應的原理不太好解釋,所以下面直接舉例說明把。假設我們有三臺服務器 servers = [A, B, C],對應的權重為 weights = [2, 5, 1]。接下來對上面的邏輯進行簡單的模擬。

    mod = 0:滿足條件,此時直接返回服務器 A

    mod = 1:需要進行一次遞減操作才能滿足條件,此時返回服務器 B

    mod = 2:需要進行兩次遞減操作才能滿足條件,此時返回服務器 C

    mod = 3:需要進行三次遞減操作才能滿足條件,經過遞減后,服務器權重為 [1, 4, 0],此時返回服務器 A

    mod = 4:需要進行四次遞減操作才能滿足條件,經過遞減后,服務器權重為 [0, 4, 0],此時返回服務器 B

    mod = 5:需要進行五次遞減操作才能滿足條件,經過遞減后,服務器權重為 [0, 3, 0],此時返回服務器 B

    mod = 6:需要進行六次遞減操作才能滿足條件,經過遞減后,服務器權重為 [0, 2, 0],此時返回服務器 B

    mod = 7:需要進行七次遞減操作才能滿足條件,經過遞減后,服務器權重為 [0, 1, 0],此時返回服務器 B

    經過8次調用后,我們得到的負載均衡結果為 [A, B, C, A, B, B, B, B],次數比 A:B:C = 2:5:1,等于權重比。當 sequence = 8 時,mod = 0,此時重頭再來。從上面的模擬過程可以看出,當 mod >= 3 后,服務器 C 就不會被選中了,因為它的權重被減為0了。當 mod >= 4 后,服務器 A 的權重被減為0,此后 A 就不會再被選中。

    以上是 2.6.4 版本的 RoundRobinLoadBalance 分析過程,大家如果看不懂,自己可以定義一些權重組合進行模擬。也可以寫點測試用例,進行調試分析,總之不要死看。

    2.6.4 版本的 RoundRobinLoadBalance 存在著比較嚴重的性能問題,該問題最初是在?issue #2578?中被反饋出來。問題出在了 Invoker 的返回時機上,RoundRobinLoadBalance 需要在mod == 0 && v.getValue() > 0?條件成立的情況下才會被返回相應的 Invoker。假如 mod 很大,比如 10000,50000,甚至更大時,doSelect 方法需要進行很多次計算才能將 mod 減為0。由此可知,doSelect 的效率與 mod 有關,時間復雜度為 O(mod)。mod 又受最大權重 maxWeight 的影響,因此當某個服務提供者配置了非常大的權重,此時 RoundRobinLoadBalance 會產生比較嚴重的性能問題。這個問題被反饋后,社區很快做了回應。并對 RoundRobinLoadBalance 的代碼進行了重構,將時間復雜度優化至了常量級別。這個優化可以說很好了,下面我們來學習一下優化后的代碼。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = "roundrobin";private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();private final ConcurrentMap<String, AtomicPositiveInteger> indexSeqs = new ConcurrentHashMap<String, AtomicPositiveInteger>();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();int length = invokers.size();int maxWeight = 0;int minWeight = Integer.MAX_VALUE;final List<Invoker<T>> invokerToWeightList = new ArrayList<>();// 查找最大和最小權重for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);maxWeight = Math.max(maxWeight, weight);minWeight = Math.min(minWeight, weight);if (weight > 0) {invokerToWeightList.add(invokers.get(i));}}// 獲取當前服務對應的調用序列對象 AtomicPositiveIntegerAtomicPositiveInteger sequence = sequences.get(key);if (sequence == null) {// 創建 AtomicPositiveInteger,默認值為0sequences.putIfAbsent(key, new AtomicPositiveInteger());sequence = sequences.get(key);}// 獲取下標序列對象 AtomicPositiveIntegerAtomicPositiveInteger indexSeq = indexSeqs.get(key);if (indexSeq == null) {// 創建 AtomicPositiveInteger,默認值為 -1indexSeqs.putIfAbsent(key, new AtomicPositiveInteger(-1));indexSeq = indexSeqs.get(key);}if (maxWeight > 0 && minWeight < maxWeight) {length = invokerToWeightList.size();while (true) {int index = indexSeq.incrementAndGet() % length;int currentWeight = sequence.get() % maxWeight;// 每循環一輪(index = 0),重新計算 currentWeightif (index == 0) {currentWeight = sequence.incrementAndGet() % maxWeight;}// 檢測 Invoker 的權重是否大于 currentWeight,大于則返回if (getWeight(invokerToWeightList.get(index), invocation) > currentWeight) {return invokerToWeightList.get(index);}}}// 所有 Invoker 權重相等,此時進行普通的輪詢即可return invokers.get(sequence.incrementAndGet() % length);} }

    上面代碼的邏輯是這樣的,每進行一輪循環,重新計算 currentWeight。如果當前 Invoker 權重大于 currentWeight,則返回該 Invoker。還是舉例說明吧,假設服務器 [A, B, C] 對應權重 [5, 2, 1]。

    第一輪循環,currentWeight = 1,可返回 A 和 B

    第二輪循環,currentWeight = 2,返回 A

    第三輪循環,currentWeight = 3,返回 A

    第四輪循環,currentWeight = 4,返回 A

    第五輪循環,currentWeight = 0,返回 A, B, C

    如上,這里的一輪循環是指 index 再次變為0所經歷過的循環,這里可以把 index = 0 看做是一輪循環的開始。每一輪循環的次數與 Invoker 的數量有關,Invoker 數量通常不會太多,所以我們可以認為上面代碼的時間復雜度為常數級。

    重構后的 RoundRobinLoadBalance 看起來已經很不錯了,但是在代碼更新不久后,很有又被重構了。這次重構原因是新的 RoundRobinLoadBalance 在某些情況下選出的服務器序列不夠均勻。比如,服務器 [A, B, C] 對應權重 [5, 1, 1]。現在進行7次負載均衡,選擇出來的序列為 [A, A, A, A, A, B, C]。前5個請求全部都落在了服務器 A上,分布不夠均勻。這將會使服務器 A 短時間內接收大量的請求,壓力陡增。而 B 和 C 無請求,處于空閑狀態。我們期望的結果是這樣的 [A, A, B, A, C, A, A],不同服務器可以穿插獲取請求。為了增加負載均衡結果的平滑性,社區再次對 RoundRobinLoadBalance 的實現進行了重構。這次重構參考自 Nginx 的平滑加權輪詢負載均衡,實現原理是這樣的。每個服務器對應兩個權重,分別為 weight 和 currentWeight。其中 weight 是固定的,currentWeight 是會動態調整,初始值為0。當有新的請求進來時,遍歷服務器列表,讓它的 currentWeight 加上自身權重。遍歷完成后,找到最大的 currentWeight,并將其減去權重總和,然后返回相應的服務器即可。

    上面描述不是很好理解,下面還是舉例說明吧。仍然使用服務器 [A, B, C] 對應權重 [5, 1, 1] 的例子進行說明,現在有7個請求依次進入負載均衡邏輯,選擇過程如下:

    請求編號currentWeight 數組選擇結果減去權重總和后的 currentWeight 數組
    1[5, 1, 1]A[-2, 1, 1]
    2[3, 2, 2]A[-4, 2, 2]
    3[1, 3, 3]B[1, -4, 3]
    4[6, -3, 4]A[-1, -3, 4]
    5[4, -2, 5]C[4, -2, -2]
    6[9, -1, -1]A[2, -1, -1]
    7[7, 0, 0]A[0, 0, 0]

    如上,經過平滑性處理后,得到的服務器序列為 [A, A, B, A, C, A, A],相比之前的序列 [A, A, A, A, A, B, C],分布性要好一些。初始情況下 currentWeight = [0, 0, 0],第7個請求處理完后,currentWeight 再次變為 [0, 0, 0],是不是很神奇。這個結果也不難理解,在7次計算過程中,每個服務器的 currentWeight 都增加了自身權重 weight * 7,得到 currentWeight = [35, 7, 7],A 被選中5次,要被減去 5 * 7。B 和 C 被選中1次,要被減去 1 * 7。于是 currentWeight = [35, 7, 7] - [35, 7, 7] = [0, 0, 0]。

    以上就是平滑加權輪詢的計算過程,現在大家應該對平滑加權輪詢算法了有了一些了解。接下來,我們來看看 Dubbo-2.6.5 是如何實現上面的計算過程的。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = "roundrobin";private static int RECYCLE_PERIOD = 60000;protected static class WeightedRoundRobin {// 服務提供者權重private int weight;// 當前權重private AtomicLong current = new AtomicLong(0);// 最后一次更新時間private long lastUpdate;public void setWeight(int weight) {this.weight = weight;// 初始情況下,current = 0current.set(0);}public long increaseCurrent() {// current = current + weight;return current.addAndGet(weight);}public void sel(int total) {// current = current - total;current.addAndGet(-1 * total);}}// 嵌套 Map 結構,存儲的數據結構示例如下:// {// "UserService.query": {// "url1": WeightedRoundRobin@123, // "url2": WeightedRoundRobin@456, // },// "UserService.update": {// "url1": WeightedRoundRobin@123, // "url2": WeightedRoundRobin@456,// }// }// 最外層為服務類名 + 方法名,第二層為 url 到 WeightedRoundRobin 的映射關系。// 這里我們可以將 url 看成是服務提供者的 idprivate ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();// 原子更新鎖private AtomicBoolean updateLock = new AtomicBoolean();@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();// 獲取 url 到 WeightedRoundRobin 映射表,如果為空,則創建一個新的ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);if (map == null) {methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());map = methodWeightMap.get(key);}int totalWeight = 0;long maxCurrent = Long.MIN_VALUE;// 獲取當前時間long now = System.currentTimeMillis();Invoker<T> selectedInvoker = null;WeightedRoundRobin selectedWRR = null;// 下面這個循環主要做了這樣幾件事情:// 1. 遍歷 Invoker 列表,檢測當前 Invoker 是否有// 對應的 WeightedRoundRobin,沒有則創建// 2. 檢測 Invoker 權重是否發生了變化,若變化了,// 則更新 WeightedRoundRobin 的 weight 字段// 3. 讓 current 字段加上自身權重,等價于 current += weight// 4. 設置 lastUpdate 字段,即 lastUpdate = now// 5. 尋找具有最大 current 的 Invoker 以及 WeightedRoundRobin,// 暫存起來,留作后用// 6. 計算權重總和for (Invoker<T> invoker : invokers) {String identifyString = invoker.getUrl().toIdentityString();WeightedRoundRobin weightedRoundRobin = map.get(identifyString);int weight = getWeight(invoker, invocation);if (weight < 0) {weight = 0;}// 檢測當前 Invoker 是否有對應的 WeightedRoundRobin,沒有則創建if (weightedRoundRobin == null) {weightedRoundRobin = new WeightedRoundRobin();// 設置 Invoker 權重weightedRoundRobin.setWeight(weight);// 存儲 url 唯一標識 identifyString 到 weightedRoundRobin 的映射關系map.putIfAbsent(identifyString, weightedRoundRobin);weightedRoundRobin = map.get(identifyString);}// Invoker 權重不等于 WeightedRoundRobin 中保存的權重,說明權重變化了,此時進行更新if (weight != weightedRoundRobin.getWeight()) {weightedRoundRobin.setWeight(weight);}// 讓 current 加上自身權重,等價于 current += weightlong cur = weightedRoundRobin.increaseCurrent();// 設置 lastUpdate,表示近期更新過weightedRoundRobin.setLastUpdate(now);// 找出最大的 current if (cur > maxCurrent) {maxCurrent = cur;// 將具有最大 current 權重的 Invoker 賦值給 selectedInvokerselectedInvoker = invoker;// 將 Invoker 對應的 weightedRoundRobin 賦值給 selectedWRR,留作后用selectedWRR = weightedRoundRobin;}// 計算權重總和totalWeight += weight;}// 對 <identifyString, WeightedRoundRobin> 進行檢查,過濾掉長時間未被更新的節點。// 該節點可能掛了,invokers 中不包含該節點,所以該節點的 lastUpdate 長時間無法被更新。// 若未更新時長超過閾值后,就會被移除掉,默認閾值為60秒。if (!updateLock.get() && invokers.size() != map.size()) {if (updateLock.compareAndSet(false, true)) {try {ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<String, WeightedRoundRobin>();// 拷貝newMap.putAll(map);// 遍歷修改,也就是移除過期記錄Iterator<Entry<String, WeightedRoundRobin>> it = newMap.entrySet().iterator();while (it.hasNext()) {Entry<String, WeightedRoundRobin> item = it.next();if (now - item.getValue().getLastUpdate() > RECYCLE_PERIOD) {it.remove();}}// 更新引用methodWeightMap.put(key, newMap);} finally {updateLock.set(false);}}}if (selectedInvoker != null) {// 讓 current 減去權重總和,等價于 current -= totalWeightselectedWRR.sel(totalWeight);// 返回具有最大 current 的 Invokerreturn selectedInvoker;}// should not happen herereturn invokers.get(0);} }

    以上就是 Dubbo-2.6.5 版本的 RoundRobinLoadBalance,大家如果能夠理解平滑加權輪詢算法的計算過程,再配合我寫的注釋,理解上面的代碼應該不難。

    以上就是關于 RoundRobinLoadBalance 全部的分析,內容有點多,大家慢慢消化吧。好了,本節先到這。

    ?3.總結

    本篇文章對 Dubbo 中的幾種負載均衡實現進行了詳細的分析,總的來說,這篇文章寫的還是有點累的。主要是每介紹一種負載均衡實現,就要介紹一下相關背景。另一方面,這里很多東西對于我來說,也完全是新的。在此之前,我對負載均衡算法并沒太多了解。這篇文章基本上是邊學邊寫的,總共耗時5天。本來想簡單寫寫算了,但最后還是決定寫詳細點。好在,現在寫完了,我也可以放松一下了。

    本篇文章是我的 Dubbo 源碼分析系列文章關于集群容錯部分的最后一篇文章,寫完感覺學到了很多東西。通過堅持不懈的閱讀代碼,寫技術文章,使得我對 Dubbo 有了更深入的了解。當然,這還遠遠不夠。后續還有很多東西要了解,比如 Nacos、Sentinel 等。長路漫漫,步履不停。

    好了,本篇文章到這里就結束了。感謝大家的閱讀。

    ?參考

    • 負載均衡之加權輪詢算法 - CSDN
    • dubbo源碼-預熱warmup過程 - 簡書
    • 一致性哈希算法原理 - 博客園

    ?附錄:Dubbo 源碼分析系列文章

    時間文章
    2018-10-01Dubbo 源碼分析 - SPI 機制
    2018-10-13Dubbo 源碼分析 - 自適應拓展原理
    2018-10-31Dubbo 源碼分析 - 服務導出
    2018-11-12Dubbo 源碼分析 - 服務引用
    2018-11-17Dubbo 源碼分析 - 集群容錯之 Directory
    2018-11-20Dubbo 源碼分析 - 集群容錯之 Router
    2018-11-24Dubbo 源碼分析 - 集群容錯之 Cluster
    2018-11-29Dubbo 源碼分析 - 集群容錯之 LoadBalance
    • 本文鏈接:?https://www.tianxiaobo.com/2018/11/29/Dubbo-源碼分析-集群容錯之-LoadBalance/

    http://www.tianxiaobo.com/2018/11/29/Dubbo-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E9%9B%86%E7%BE%A4%E5%AE%B9%E9%94%99%E4%B9%8B-LoadBalance/?

    總結

    以上是生活随笔為你收集整理的Dubbo 源码分析 - 集群容错之 LoadBalance的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    国产黄色片免费看 | 波多野结衣视频一区二区 | 亚洲高清激情 | 91九色蝌蚪| 麻豆视频国产精品 | 欧美精品999| 一区二区三区四区久久 | 国产精品久久久毛片 | 国产精品手机在线 | 亚洲精品456在线播放 | 欧美日韩视频观看 | 五月天婷亚洲天综合网鲁鲁鲁 | 黄色片网站免费 | av在线进入 | 国产精品网址在线观看 | 日韩精品一区二区三区免费观看视频 | 天天av在线播放 | 日韩精品一区二区三区外面 | 日韩动漫免费观看高清完整版在线观看 | 国产精品成人自产拍在线观看 | 久久免费视屏 | 中文字幕免费一区 | 中文字幕一区二区三区精华液 | 国产日韩精品一区二区三区在线 | 四虎影视精品永久在线观看 | www.久久色 | jizz欧美性9 国产一区高清在线观看 | 四虎成人在线 | 日韩高清黄色 | 麻豆影视在线播放 | 在线观看视频你懂的 | 久久国产一区二区 | 色国产精品一区在线观看 | 欧美日韩不卡在线视频 | 国产一级h | 少妇av片 | 国产精品青草综合久久久久99 | 免费在线观看不卡av | 久久99久久99久久 | 三级黄在线 | 黄色片免费在线 | 欧美日韩裸体免费视频 | 国产不卡视频 | 91九色丨porny丨丰满6 | 成人av一区二区三区 | 91系列在线 | 成人蜜桃视频 | 激情丁香久久 | 日韩一区二区三区免费视频 | 欧美少妇的秘密 | 日韩精品在线免费观看 | 亚洲国产资源 | 亚洲欧美日韩精品久久久 | 日韩黄色一区 | 91成人在线看| 在线观看福利网站 | 国产午夜精品免费一区二区三区视频 | 中文区中文字幕免费看 | 久久综合电影 | 国产麻豆精品久久一二三 | 精品国产乱码久久久久久三级人 | 欧美日韩3p | 国产激情小视频在线观看 | 天天激情 | 伊人日日干 | 夜夜躁日日躁狠狠久久av | 高清精品视频 | 成年人视频在线免费 | 久久手机在线视频 | 国产欧美久久久精品影院 | 国产精品午夜在线 | 色资源网免费观看视频 | 天天综合久久综合 | 91精品1区2区 | 最新中文字幕在线观看视频 | 成人在线小视频 | 欧美日韩一区二区三区视频 | 国产精品久久久久久久久久不蜜月 | 国产日韩欧美在线 | 国产99亚洲 | 亚洲视频免费在线观看 | 日韩av成人在线观看 | 欧美久久电影 | 日韩精品免费一线在线观看 | 精品视频久久 | 特级西西444www高清大视频 | 99久久综合国产精品二区 | 国产色影院 | 黄色av免费电影 | 91禁看片| 精品在线一区二区 | 久久久久综合精品福利啪啪 | 亚洲精品视频在线观看视频 | 久久精品国产一区二区三 | 日韩精品1区2区 | 中文字幕最新精品 | 91在线91| av 在线观看 | 欧美特一级片 | 欧美看片 | 日韩欧美区 | 最新黄色av网址 | 黄色av影视 | 日韩一级片大全 | 国产黄色成人av | 精品视频成人 | 欧美色综合久久 | 波多野结衣亚洲一区二区 | 免费看片亚洲 | 日本精品在线 | www.干| 久久精品影视 | 久久久精品高清 | 五月婷婷在线观看视频 | 久草视频一区 | 国产成人久 | 国产五月天婷婷 | 中文字幕在线观看1 | avsex| 97热久久免费频精品99 | 日韩在线观看的 | 国产99久久精品 | 日韩久久精品一区 | 亚欧洲精品视频在线观看 | 中文字幕在线观看不卡 | 狠狠干天天操 | 久久中文字幕导航 | 国产91对白在线播 | 免费福利片2019潦草影视午夜 | 91电影福利 | 国产一区二区三区四区在线 | 在线成人一区二区 | 亚洲高清资源 | 中文av免费 | av电影一区 | 国产日韩欧美视频 | 在线国产欧美 | 99精品国产成人一区二区 | 91免费视频网站在线观看 | 精品免费一区 | 不卡av免费在线观看 | 一区二区中文字幕在线播放 | 国产免费av一区二区三区 | 国产一区国产精品 | 国产又黄又爽又猛视频日本 | 久久99精品国产麻豆婷婷 | 色视频国产直接看 | 国产亚洲精品久久久久久电影 | 欧美另类tv | 国产馆在线播放 | 久久久久久久久久久久av | 欧美精品久久久 | av超碰在线 | 中文av资源站 | 视频国产区 | 黄色小说免费观看 | www天天干com | 久草观看 | 国产精品久久久久aaaa | 综合久久2023| 国产视频97 | 国产中文字幕亚洲 | 亚洲另类交 | 99热九九这里只有精品10 | 成人蜜桃视频 | 黄色三几片 | 五月婷婷六月丁香在线观看 | 美女视频永久黄网站免费观看国产 | 色婷婷久久一区二区 | 日韩 在线 | 久久色亚洲 | 天天干天天操天天做 | 成人在线观看免费视频 | 在线观看国产成人av片 | 在线中文字母电影观看 | 天天射日| 久在线观看视频 | 福利视频精品 | 日韩久久久久久久 | 18岁免费看片 | 97电影在线看视频 | 国产精品大尺度 | 色在线中文字幕 | 欧美91av | 黄色免费高清视频 | www五月婷婷 | 日韩在线电影一区二区 | 久久精品三 | 91在线在线观看 | 超碰在线日本 | 国产99色| 亚洲另类视频在线 | 成人a在线观看高清电影 | 亚洲毛片久久 | 欧美日韩中文字幕视频 | 精品久久网 | 伊人色综合久久天天网 | av在线一二三区 | 美女久久视频 | 久草在线视频在线 | 久久狠狠一本精品综合网 | 成人天堂网| 亚洲影视九九影院在线观看 | 久99久视频 | 亚洲欧美va | 欧美激情综合五月色丁香 | 免费看的毛片 | 一区二区三区高清在线 | 一区二区在线影院 | 一区二区三区韩国免费中文网站 | 精品福利在线观看 | 国产在线日本 | 国产精品第2页 | 91福利视频久久久久 | 亚洲国产精品99久久久久久久久 | 91免费高清 | 99视频国产精品免费观看 | 国产精品露脸在线 | 天天做综合网 | av看片网址 | 国产91精品一区二区绿帽 | 亚洲视频精选 | 五月天婷亚洲天综合网精品偷 | 国偷自产中文字幕亚洲手机在线 | 五月色综合 | 亚洲成人黄色网址 | 久久九九影院 | 成年人黄色在线观看 | 国产 一区二区三区 在线 | 国产视频97 | 91精品在线看 | 六月丁香社区 | 波多野结依在线观看 | 在线观看日韩专区 | 亚洲久久视频 | 97人人澡人人添人人爽超碰 | 91探花国产综合在线精品 | 手机在线看永久av片免费 | 免费a一级| av免费在线网站 | 97在线视频观看 | 狠狠躁日日躁狂躁夜夜躁 | 国产精品1区2区3区 久久免费视频7 | 综合久久久久久 | 偷拍精偷拍精品欧洲亚洲网站 | 在线免费观看黄色小说 | 中文字幕在线观看免费高清完整版 | 久久久亚洲麻豆日韩精品一区三区 | 久久99亚洲网美利坚合众国 | 国产精品一区二区久久精品爱微奶 | 在线观看国产www | 国产涩涩在线观看 | 国产免费又粗又猛又爽 | 国产一区欧美日韩 | 国产在线a| 在线观看av免费观看 | 久热只有精品 | 免费看的国产视频网站 | 激情图片qvod | 国产精品乱码一区二三区 | 国产日韩精品在线 | 欧美日韩亚洲在线观看 | 91爱爱免费观看 | 久久歪歪| 精品乱码一区二区三四区 | 成人国产网站 | 婷婷久久丁香 | 久久99精品国产麻豆宅宅 | 国内久久视频 | 欧美极品在线播放 | 超碰人人舔 | 国产精成人品免费观看 | 九九在线精品视频 | 国产精品久久久久久久婷婷 | 欧美三级在线播放 | 久青草视频 | 久久涩涩网站 | 亚洲精品字幕在线 | 成人黄色在线观看视频 | 黄色网址中文字幕 | 人人插人人草 | 91国内在线视频 | 国产精品99精品 | 五月婷婷久草 | 国产精品女人久久久 | 日韩免费小视频 | 国产精品国产三级国产不产一地 | 在线观看亚洲a | 亚洲黄电影 | 日韩高清国产精品 | 亚洲色图美腿丝袜 | 欧美xxxxx在线视频 | 激情动态| 久久黄色影院 | www.色com| 色视频在线观看 | 一本色道久久精品 | 午夜国产福利在线 | 日韩高清www | 国产91九色视频 | 欧美视频在线观看免费网址 | 国产精品一区一区三区 | 日韩v在线91成人自拍 | 中文字幕在线观看网站 | 在线观看日韩视频 | 亚洲黄色在线播放 | 99精品免费在线观看 | 又爽又黄又刺激的视频 | 国产小视频在线播放 | 国产麻豆精品久久一二三 | 国产精品av免费 | 日日操狠狠干 | 国产亚洲人 | 天天干,天天射,天天操,天天摸 | 日韩精品一区二区在线视频 | 黄色小网站免费看 | 成人在线观看资源 | 中文字幕一区二区在线播放 | 97成人资源站 | 国产精品精品国产色婷婷 | 欧美成人性战久久 | 97国产大学生情侣酒店的特点 | av电影不卡在线 | 亚洲综合婷婷 | 日本三级中文字幕在线观看 | 在线观看视频色 | 天天操夜夜做 | 成人蜜桃网 | 亚洲高清av| 色国产精品 | 日日夜夜天天综合 | 午夜电影一区 | 国产成人一区二区三区影院在线 | 一区二区精品在线 | 特黄特色特刺激视频免费播放 | 成人中心免费视频 | 成人国产精品久久久久久亚洲 | 日日草视频| 91在线网址 | 91最新地址永久入口 | 免费看wwwwwwwwwww的视频 久久久久久99精品 91中文字幕视频 | 色综合色综合色综合 | 国产亚洲精品久久久久久大师 | 国产在线一线 | 国内三级在线 | www视频免费在线观看 | 天天爱天天操天天射 | 午夜在线资源 | 91精品国产乱码久久 | 国产色在线观看 | 国产精品www | 激情丁香在线 | 免费精品视频 | 亚洲精品毛片一级91精品 | 久久午夜电影院 | 操操操天天操 | 国产女人18毛片水真多18精品 | 欧美日韩一区二区三区在线观看视频 | 精品一区电影国产 | 亚洲视频 在线观看 | 久久五月激情 | 在线色网站 | 全久久久久久久久久久电影 | 午夜久久网 | 午夜aaaa| 日韩在线资源 | 日韩一级片网址 | 免费国产一区二区 | 日韩精品中文字幕在线 | 日本成人a | 国产精品福利无圣光在线一区 | 五月天中文在线 | 成年人免费在线观看 | 日本丶国产丶欧美色综合 | 国产va精品免费观看 | av天天澡天天爽天天av | 日韩激情中文字幕 | 免费精品国产 | 一区二区电影网 | 特级西西www44高清大胆图片 | 免费观看日韩 | 依人成人综合网 | 天天添夜夜操 | 国产小视频免费在线观看 | 天天干 天天摸 天天操 | 中文字幕免费高清 | 狠狠干天天 | 在线观看免费av片 | 在线电影 你懂得 | 日韩高清激情 | 久久国产精品免费视频 | 日韩视频专区 | 国产精品久久久久久久久搜平片 | 狠狠操在线 | 国产精品欧美久久久久久 | 久久伦理电影 | 精品国产一区二区久久 | 日韩精品免费在线观看 | 日本高清中文字幕有码在线 | 激情小说网站亚洲综合网 | 中文字幕国产一区 | 97超碰中文字幕 | 9色在线视频 | 99精彩视频在线观看免费 | 天堂入口网站 | 精品亚洲一区二区三区 | 国产精品久久久久久久久免费看 | 在线视频国产区 | 91最新国产 | 久一久久| 国产亚洲精品久久久网站好莱 | 在线国产欧美 | 精品在线99| 亚洲国产精品va在线看黑人 | 国产一区二区三区黄 | 色多视频在线观看 | 免费观看av| 99福利影院 | 超碰资源在线 | 日韩乱码在线 | 夜夜操天天操 | 不卡国产视频 | 97电影在线观看 | 亚洲在线观看av | 麻豆传媒电影在线观看 | 在线播放 日韩专区 | 久久国产乱 | 在线免费三级 | 国产成人精品在线播放 | 国产一级做a爱片久久毛片a | 91污在线观看 | 日日干天夜夜 | 国产精品毛片一区二区三区 | 久久精品国产v日韩v亚洲 | 欧美日韩观看 | 蜜臀久久99精品久久久无需会员 | 西西大胆啪啪 | 久久精品高清视频 | 欧美日韩亚洲在线 | 久久免费一 | 欧美一区日韩精品 | 国产成人精品电影久久久 | 99热国产在线观看 | 日本少妇高清做爰视频 | 韩日色视频 | www.伊人网 | 午夜久久福利视频 | 久久久精品小视频 | 五月激情天 | 久草精品视频在线播放 | 国内精品视频一区二区三区八戒 | 国产剧情一区二区在线观看 | 国产超碰在线观看 | 91网页版在线观看 | 亚州精品天堂中文字幕 | 精品一区在线 | 国产精品久久一区二区无卡 | 午夜久操 | 精品国产乱码一区二区三区在线 | 日本一区二区三区免费观看 | 国产精品久久久久久久电影 | 欧美日韩后 | 亚洲高清免费在线 | 97精品超碰一区二区三区 | 韩国av一区二区三区在线观看 | 国产福利精品一区二区 | 最新日韩精品 | 久久精品91久久久久久再现 | a成人v在线| 操操操日日日干干干 | 成人黄色在线电影 | 天天弄天天干 | 日韩在线观看视频网站 | 国产一区二区免费 | 欧美日韩亚洲在线 | 视频精品一区二区三区 | 免费在线观看日韩 | 久草电影免费在线观看 | 美女网站视频一区 | 久久免费视频一区 | 在线观看国产永久免费视频 | 国产精品欧美 | 国产精品久久久久免费 | 91在线看视频 | 国产一区二区三区在线免费观看 | 国产精品专区在线观看 | 波多野结衣小视频 | 亚洲六月丁香色婷婷综合久久 | 操操综合 | 日韩另类在线 | 在线免费性生活片 | 片网站| 日操操 | 日本精品视频在线观看 | 手机成人av | 国产精品久久久久av | 五月婷婷色丁香 | 日韩精品影视 | 九九九九免费视频 | 国产精品视频久久久 | 一级a性色生活片久久毛片波多野 | 日韩天堂网 | 欧美黑人xxxx猛性大交 | 欧美久久精品 | 就要干b| 国产一区二区不卡视频 | 国产精品地址 | 综合久久一本 | 国产精品一区二区在线观看免费 | 国产成人精品一区二区三区免费 | 综合在线亚洲 | 99久久这里有精品 | 蜜桃传媒一区二区 | 免费观看91视频大全 | 国产精品一区二区久久精品 | 免费看国产精品 | japanesexxxxfreehd乱熟 | 国产午夜精品久久 | 在线观看免费黄色 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 国产一级淫片在线观看 | 免费黄色看片 | 久久综合九色综合欧美狠狠 | 激情视频区 | 高清av网站 | 天天躁天天狠天天透 | 人人插人人舔 | 日韩久久精品一区二区 | 国产一级大片在线观看 | 国产美女精品视频免费观看 | 久久成人资源 | 日韩有码网站 | 中文字幕在线播放视频 | www.com.日本一级 | 欧美另类v | 色婷丁香| 亚洲在线网址 | 大型av综合网站 | 天天操天天摸天天干 | 久久久久久久久久久影院 | 国产日韩欧美在线观看 | 国产视频二 | 国产精品美女久久久久久2018 | 亚洲一区二区三区在线看 | 亚洲精品乱码久久久久久蜜桃91 | 久久精品视频国产 | 日韩免费三区 | 欧美性脚交 | 国产精品第54页 | 在线a人v观看视频 | 黄色免费在线看 | 色吊丝av中文字幕 | 久久综合婷婷国产二区高清 | 夜夜操网站 | 亚洲精品高清视频在线观看 | 免费激情在线电影 | 97精品免费视频 | av大全在线 | 国产精品99久久久精品免费观看 | 亚洲综合在线一区二区三区 | 日韩免费观看av | www.eeuss影院av撸 | 成人一区二区三区中文字幕 | 激情六月婷婷久久 | 四虎国产精品成人免费影视 | 夜夜爽88888免费视频4848 | av在线免费观看黄 | 国产精品美女久久久久久久久久久 | 久久xxxx| 91精品国产92久久久久 | 色综合久久88色综合天天 | 日韩av电影网站在线观看 | www.天天色.com | 国产高清视频在线播放一区 | 亚州欧美精品 | 黄色毛片观看 | 欧美视频国产视频 | 色a在线观看 | 天天色天天操天天爽 | 国产精品色在线 | 黄色成人毛片 | 欧美一级片免费在线观看 | 日韩理论在线视频 | 欧美日韩免费一区二区三区 | 午夜精品久久久久久久久久久久久久 | 亚洲美女精品 | 天天操天天添 | 久久久精品免费观看 | 在线视频1卡二卡三卡 | 国产 在线 高清 精品 | av高清不卡 | 亚洲精品中文字幕在线 | 在线观看v片 | 欧美日韩成人 | 97在线免费视频观看 | 亚洲人成免费网站 | 亚洲久在线 | 在线观看黄色 | 久久精品久久精品久久精品 | 青青草在久久免费久久免费 | 精品国产一区二区三区久久 | 国产黄色精品视频 | 黄色网在线免费观看 | 国产中文字幕91 | 久久综合九色欧美综合狠狠 | 日韩欧美视频二区 | 日日日爽爽爽 | 一区二区三区免费在线播放 | 亚洲天堂精品视频在线观看 | 91最新在线视频 | 久久久久一区二区三区四区 | 日本巨乳在线 | 9草在线 | 成人理论电影 | 日本最新高清不卡中文字幕 | 超碰在线中文字幕 | www.亚洲精品在线 | 少妇性xxx| 精品国产大片 | 国产精品视频地址 | 国产专区视频在线 | 天天干天天干天天干天天干天天干天天干 | 综合网成人| 国产高清在线免费 | 国产午夜精品一区 | 日韩av高潮 | 福利一区在线视频 | 97人人澡人人爽人人模亚洲 | 国产九九精品视频 | 97成人精品 | 99夜色| 国产视频精品视频 | 免费久久久久久 | 亚洲精品免费在线观看 | 亚洲国产色一区 | 久久久久女人精品毛片 | 五月天亚洲综合小说网 | 久久久久久久电影 | 午夜国产福利在线 | 国产免费av一区二区三区 | 国产美女精品视频 | 伊人婷婷网| 欧洲亚洲激情 | av在线播放快速免费阴 | 成年性视频 | 亚洲一区二区精品3399 | 成人动漫一区二区三区 | 欧美日韩国产一二三区 | 久久综合天天 | 中文免费观看 | 在线观看911视频 | 九九综合九九综合 | 青青河边草观看完整版高清 | 国产精品毛片久久久久久久 | 99精品国产在热久久下载 | 992tv在线观看网站 | 中文字幕中文字幕在线中文字幕三区 | 国产精品免费视频一区二区 | 国产亚洲视频在线免费观看 | 一区国产精品 | 国产原创在线观看 | 欧美精品在线观看一区 | 中文字幕之中文字幕 | 黄色免费高清视频 | 精品麻豆入口免费 | 久久综合九色综合久久久精品综合 | 夜夜干天天操 | 成人免费观看网址 | 免费色视频网站 | 国产亚洲精品久久久久动 | 91视频-88av| 日韩欧美xxx | 毛片无卡免费无播放器 | 日批视频国产 | 91中文字幕网 | 成人av影视 | 欧美日韩在线网站 | 97超碰总站| 亚洲一区二区三区精品在线观看 | 狠狠操天天干 | 亚洲一区二区三区91 | 国产爽视频 | 日本在线观看一区 | 日韩电影在线观看中文字幕 | 伊人五月天婷婷 | 欧美一级片在线免费观看 | 丁香久久激情 | 欧美一二三区在线观看 | 91高清一区| 久久精品在线免费观看 | 美女久久一区 | 日本精品一区二区三区在线播放视频 | 91欧美日韩国产 | 欧美日韩国产一区二区三区在线观看 | 久久96国产精品久久99漫画 | 9热精品 | 一本一本久久a久久精品牛牛影视 | 精品欧美小视频在线观看 | 欧美成人h版在线观看 | av福利免费 | 黄污视频网站 | 在线视频一区观看 | 毛片a级片 | 亚洲va欧美va人人爽春色影视 | 日韩av手机在线看 | 99婷婷 | 久久高视频 | 久久久久久久久网站 | 九九免费在线观看视频 | 久久色视频| www.色综合.com | 成人aⅴ视频 | 免费a一级| 亚洲国产免费 | 女人久久久久 | 国产精品久久久久久久久久久杏吧 | 久久国产精品精品国产色婷婷 | 最新影院| 亚洲精品黄色 | 91久久久国产精品 | 人人爽网站 | 国产裸体视频网站 | 日韩在线视频免费看 | 国产小视频在线观看免费 | 超碰在线人| 国产成人一级电影 | 欧美久久久影院 | 韩国三级av在线 | 91精品国产麻豆国产自产影视 | 国产视频久久 | 久久精品亚洲一区二区三区观看模式 | 99热在线精品观看 | 国产精品短视频 | 色婷婷啪啪免费在线电影观看 | 成年人在线免费看视频 | 色综合久久88色综合天天 | 99精品欧美一区二区三区黑人哦 | av在线免费网站 | 久久99久久99精品免视看婷婷 | 国产欧美精品一区二区三区 | 国产精品一区二区三区视频免费 | 国产精品网址在线观看 | 欧美日韩大片在线观看 | 91探花国产综合在线精品 | 天天干天天射天天爽 | 亚洲国产精品小视频 | 深爱激情五月综合 | 日日碰狠狠躁久久躁综合网 | 特级黄色片免费看 | 免费观看www视频 | 国产免费黄视频在线观看 | 在线视频观看成人 | 久久亚洲欧美日韩精品专区 | 国模一区二区三区四区 | 久久美女电影 | 2019中文在线观看 | 国产91精品久久久久 | 黄色a视频免费 | 中文字幕亚洲欧美日韩 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 国产精品一区二区三区在线看 | 国产无吗一区二区三区在线欢 | 久久精品欧美一 | 91成人免费看 | 69xxxx欧美| 久热免费在线 | 一区二区 精品 | 17婷婷久久www | 亚洲精品久久久久久国 | 国产精品一区二区久久 | 日韩在线二区 | 天天操天天操天天操天天操天天操天天操 | 亚洲精品乱码久久久久久蜜桃欧美 | 午夜精品久久久久久久99无限制 | 超碰大片 | 亚洲视频久久久 | 中文字幕乱码日本亚洲一区二区 | 91精品在线视频观看 | 成人黄在线观看 | 国产做aⅴ在线视频播放 | 99精品福利 | 天天干,狠狠干 | 狠狠干狠狠色 | av免费高清观看 | 日韩在线观看一区二区三区 | 日本精a在线观看 | 97夜夜澡人人双人人人喊 | 91在线视频一区 | 久草在线免费在线观看 | 久久久久久久综合色一本 | 97精品超碰一区二区三区 | 国产高清视频免费观看 | 黄色一级大片在线观看 | 天天色天天爱天天射综合 | 久久激情五月婷婷 | 天天曰 | 激情视频免费观看 | 97超碰超碰久久福利超碰 | 成人黄色av免费在线观看 | 99热国产在线 | 国产精品乱码久久久久 | 久久久69| 天天搞天天 | 在线高清| 久人人 | 亚洲国产精品一区二区尤物区 | 日本bbbb摸bbbb | 国产一区免费在线 | 国产日韩精品一区二区 | 精品国产乱码一区二区三区在线 | 久久午夜精品影院一区 | 丁香婷婷激情网 | 成人免费大片黄在线播放 | 国产1区2区 | 亚洲 欧洲av | 97超碰在线播放 | 国产精品情侣视频 | 国产精品视频免费看 | 日韩高清无线码2023 | 久久久免费观看完整版 | 精品视频国产一区 | 黄色网址在线播放 | 色婷婷综合久久久中文字幕 | 国产xxxx | 国产区在线视频 | 国产一区国产二区在线观看 | 美女又爽又黄 | 久久福利电影 | 日本视频久久久 | 日韩有码网站 | 在线视频 你懂得 | 欧美xxxx性xxxxx高清 | 欧美综合色 | 在线视频 你懂得 | 麻豆视频成人 | 国产一二三区在线观看 | 亚洲精品视频在线观看视频 | 国产专区第一页 | 黄色的网站免费看 | 国产精品视频大全 | 在线超碰av | 免费观看黄色12片一级视频 | 黄色毛片电影 | 97超碰在线久草超碰在线观看 | 久久人人爽人人爽人人 | 欧美日韩免费一区 | av爱干 | 日韩a级免费视频 | 精品视频免费播放 | 天天操天天射天天爽 | 国产成人三级在线观看 | 91麻豆高清视频 | 五月天婷婷在线播放 | 手机成人在线电影 | 91成人精品一区在线播放69 | 国产一级高清 | 久久久国产高清 | 欧美久久成人 | 亚洲 欧美 国产 va在线影院 | 狠狠操综合 | 国产成人91 | 午夜婷婷在线播放 | 精品国产视频在线观看 | 亚洲第一久久久 | 色噜噜色噜噜 | 黄色91在线观看 | 91影视成人| www.国产精品 | 国产高清成人 | 999久久久久久久久6666 | 中文av免费| 天天天干夜夜夜操 | 一级片视频免费观看 | 成年人免费av网站 | 国产精品一区二区三区在线看 | 精品国内 | www视频在线播放 | 亚洲视频在线观看免费 | 免费看短 | 久久久电影 | 亚洲成人黄色 | 日韩久久视频 | 亚洲日本va中文字幕 | 五月婷婷在线观看视频 | 欧美日韩中文字幕综合视频 | 337p日本欧洲亚洲大胆裸体艺术 | 激情影音 | 国产精品理论片在线观看 | 日日操操操 | 四虎www | 亚洲 欧美变态 另类 综合 | 伊人成人激情 | 欧美久久久 | 日韩av电影一区 | 日韩精品久久久久久久电影99爱 | 区一区二区三区中文字幕 | 日韩av免费大片 | 一级片视频免费观看 | 久久国产欧美日韩精品 | 西西www4444大胆在线 | 亚洲国产成人精品电影在线观看 | 伊人导航| 狠狠躁18三区二区一区ai明星 | 色五月成人 | 一区二区精品久久 | 久久99亚洲精品久久 | 二区视频在线 | 欧美一区二区三区在线 | 一区二区三区免费在线 | 国产一在线精品一区在线观看 | 久久久久久久久久久久久国产精品 | 一区二区三区精品在线视频 | 中文字幕专区高清在线观看 | 天天操天天爱天天爽 | 人人看人人 | 91大片网站 | 成人h电影 | 一区中文字幕 | 久久精品xxx | 国产欧美精品一区二区三区四区 | 欧美久久99| 免费高清在线视频一区· | 成人一区影院 | 激情婷婷欧美 | 又污又黄的网站 | 中文字幕av播放 | 91亚洲精品久久久中文字幕 | 欧美久久精品 | 欧美另类老妇 | 久久久久伦理电影 | 午夜视频在线观看一区二区 | 国产一级视屏 | 中文日韩在线 | 爱爱一区 | 久久久久久久久久免费 | 日本一区二区高清不卡 | 麻豆传媒视频观看 | 午夜精品999| 久久国内免费视频 | 区一区二区三区中文字幕 | 综合天天 | 国产精品永久久久久久久久久 | 亚洲人人av| 国产视频精品在线 | 超碰97在线人人 | 天天操天天射天天爽 | 韩日av在线 | 欧美日韩一区二区三区免费视频 | 日韩免费电影网 | 国产高清免费视频 | 国产资源站 | 成 人 黄 色 片 在线播放 | 精品特级毛片 | 97精品国产91久久久久久 | 国产亚洲精品久久久久久网站 | 欧美综合在线视频 | 久久一区国产 | 91精品啪在线观看国产81旧版 | 波多野结依在线观看 | 狠狠黄 | 久草久视频 | 青青五月天| 在线观看电影av | 亚洲国产精彩中文乱码av | 福利视频 | 免费看wwwwwwwwwww的视频 久久久久久99精品 91中文字幕视频 | 国产一区二区播放 | 日日爱夜夜爱 | 在线精品视频免费观看 | 国产裸体视频网站 | www.亚洲视频 | 99久久精品久久久久久清纯 | 久久成视频 | 91精品国产91热久久久做人人 | 一区二区三区国产精品 | 五月色丁香 | 中文在线8资源库 | 国产剧情亚洲 | 亚洲精品tv| 激情欧美一区二区三区免费看 | 免费黄色网址网站 | 在线免费av网站 | 在线观看黄色大片 | 久久视频国产精品免费视频在线 | 麻豆久久精品 | 亚洲欧洲国产视频 | 久久国产免费视频 | 欧美日韩中文字幕在线视频 | 91正在播放 | 亚洲成人精品久久久 | avav片 | 91精品国产91久久久久福利 | 国产精品视频免费看 | 国产在线精品一区二区三区 | 波多野结衣久久资源 | 国产成在线观看免费视频 | 中文字幕亚洲综合久久五月天色无吗'' | 一区二区三区四区久久 |