<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      負載均衡-一致性Hash算法

      1. Hash算法

      哈希(Hash)也稱為散列,把任意長度的輸入,通過散列算法變換成固定長度的輸出,該輸出就是散列值、哈希值(hashCode)。(來自:百度百科)

      在現(xiàn)實中,設計者常常將散列值作為索引,用于快速定位數(shù)據(jù)的位置,比如 HashMap

      // cache => key:userId, value:phone
      Map<String, String> cache = new HashMap<>();
      cache.put("user:001","159xxxx0001");
      cache.put("user:002","159xxxx0002");
      cache.put("user:003","159xxxx0003");
      
      // 查詢 "user:001" 的手機號
      String phone = cache.get("user:001");
      

      為了引入一致性Hash算法,我需要舉個例子:


      現(xiàn)在 A公司 發(fā)展良好,上億的用戶量,架構(gòu)師設計出一個方案:根據(jù)用戶id分庫,通過對用戶id進行Hash運算,計算出一個散列值,來決定用戶數(shù)據(jù)存儲在哪一個節(jié)點。

      由此出現(xiàn)一個問題,怎么才能保證數(shù)據(jù)均勻分布在各個節(jié)點?假如全部數(shù)據(jù)都存儲在 節(jié)點1 這個分庫就是失敗的,和不分庫一模一樣不是嗎?這種狀況專業(yè)術(shù)語叫數(shù)據(jù)傾斜。


      那怎么才能均勻存儲在各個節(jié)點呢?答案是 1.選擇合適的數(shù)據(jù)作為key、2.設計優(yōu)秀的散列函數(shù)。


      跑題了,今天要講的是負載均衡。

      下文舉例中,hash算法選最簡單的取余法,方便理解


      如圖所示、顯然、易得,上圖中,有四個請求,有三個節(jié)點,我們該怎么讓請求均勻的打在節(jié)點上?這是不是和上面根據(jù)用戶ID分庫的栗子有共同之處,首先需要選擇一個合適的數(shù)據(jù)當作key,還有一個優(yōu)秀的散列函數(shù),但是這很難很好的實現(xiàn)。

      問題1:如何讓請求均勻命中節(jié)點?

      如果我現(xiàn)在加一個節(jié)點,user:004將會映射在 節(jié)點0上(注:4%4=0),由于user:004以前將數(shù)據(jù)存儲在節(jié)點1,那么將查詢不到 user:004的數(shù)據(jù)。

      問題2:如何解決動態(tài)增加、減少節(jié)點帶來的問題?


      2. 一致性Hash算法

      背景之類的東西就跳過了。

      上面的散列函數(shù)是用戶id%節(jié)點數(shù),節(jié)點數(shù)是會動態(tài)增減的,那我們把節(jié)點數(shù)設置為一個固定的大數(shù)(2^32),這樣就解決了動態(tài)增加、減少節(jié)點帶來的問題。

      再上圖:

      解釋一下,就是將散列函數(shù)變?yōu)?用戶id % \(2^{32}\),如果散列值落在 節(jié)點0節(jié)點1 之間,那么我們選擇 節(jié)點1 ,同理,如果落在 節(jié)點1節(jié)點2 之間,我們選擇 節(jié)點2 ,我們也稱這個環(huán)為Hash環(huán)。


      服務器減少:

      節(jié)點1 掛掉后,其余節(jié)點依舊能正常工作,只不過原本打在 節(jié)點1 的請求,按照邏輯,打在了 節(jié)點2 上,所以需要將 節(jié)點1 的數(shù)據(jù)全部分配在節(jié)點2,這可能造成 節(jié)點2 短時間接收大量請求,節(jié)點2 也掛掉,然后導致請求全部打在 節(jié)點0,從而形成雪崩效應,全部節(jié)點掛掉。

      服務器增加:

      增加 節(jié)點4 , 只需將原本在 節(jié)點2 的部分數(shù)據(jù)重新分配在 節(jié)點4


      上面解決了,增加節(jié)點與減少節(jié)點,節(jié)點數(shù)據(jù)的問題,但是沒解決一個問題就是,數(shù)據(jù)傾斜的問題。

      如這圖,存儲在 節(jié)點2 的數(shù)據(jù)概率遠多于 節(jié)點0,根據(jù)概率論,極大可能會造成數(shù)據(jù)傾斜問題。


      解決這個辦法的問題是:添加虛擬節(jié)點。

      發(fā)光的節(jié)點為真實節(jié)點,不發(fā)光節(jié)點為虛擬節(jié)點,這樣一操作,眼睛看著都均勻了,當然虛擬節(jié)點越多越均勻(概率論問題),假如請求命中虛擬節(jié)點,會將請求轉(zhuǎn)發(fā)至真實節(jié)點,不理解了吧。

      // 返回一個鍵大于等于給定鍵的最小鍵值對的Entry對象。如果沒有這樣的鍵值對存在,則返回null。
      Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
      

      看個源碼吧:

      /*
       * Licensed to the Apache Software Foundation (ASF) under one or more
       * contributor license agreements.  See the NOTICE file distributed with
       * this work for additional information regarding copyright ownership.
       * The ASF licenses this file to You under the Apache License, Version 2.0
       * (the "License"); you may not use this file except in compliance with
       * the License.  You may obtain a copy of the License at
       *
       *     http://www.apache.org/licenses/LICENSE-2.0
       *
       * Unless required by applicable law or agreed to in writing, software
       * distributed under the License is distributed on an "AS IS" BASIS,
       * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       * See the License for the specific language governing permissions and
       * limitations under the License.
       */
      package org.apache.dubbo.rpc.cluster.loadbalance;
      
      import org.apache.dubbo.common.URL;
      import org.apache.dubbo.common.io.Bytes;
      import org.apache.dubbo.rpc.Invocation;
      import org.apache.dubbo.rpc.Invoker;
      import org.apache.dubbo.rpc.support.RpcUtils;
      
      import java.util.List;
      import java.util.Map;
      import java.util.TreeMap;
      import java.util.concurrent.ConcurrentHashMap;
      import java.util.concurrent.ConcurrentMap;
      
      import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
      import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
      
      /**
       * ConsistentHashLoadBalance
       */
      public class ConsistentHashLoadBalance extends AbstractLoadBalance {
          public static final String NAME = "consistenthash";
      
          /**
           * Hash nodes name
           */
          public static final String HASH_NODES = "hash.nodes";
      
          /**
           * Hash arguments name
           */
          public static final String HASH_ARGUMENTS = "hash.arguments";
      
          private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
      
          @SuppressWarnings("unchecked")
          @Override
          protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
              // dubbo 把節(jié)點包裝成Invoker,invokers 就相當于節(jié)點列表
              String methodName = RpcUtils.getMethodName(invocation);
              // eg. com.example.service.getUser 每個方法,對應一個selector
              String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
              // invokers.hashCode() 也就說是節(jié)點列表的HashCode
              int invokersHashCode = invokers.hashCode();
              ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
              // 如果selector==null,說明還未初始化,如果selector.identityHashCode != invokersHashCode,說明增加或者減少了節(jié)點。
              if (selector == null || selector.identityHashCode != invokersHashCode) {
                  selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, invokersHashCode));
                  selector = (ConsistentHashSelector<T>) selectors.get(key);
              }
              return selector.select(invocation);
          }
      
          private static final class ConsistentHashSelector<T> {
      
              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) {
                  // 虛擬節(jié)點Map
                  this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
                  // ConsistentHashSelector 的唯一標識,假如增加、減少節(jié)點,那么唯一標識會發(fā)生改變
                  this.identityHashCode = identityHashCode;
                  URL url = invokers.get(0).getUrl();
                  // 虛擬節(jié)點數(shù),默認160
                  this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
                  String[] index = 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]);
                  }
                  // 將虛擬節(jié)點放進virtualInvokers
                  for (Invoker<T> invoker : invokers) {
                      String address = invoker.getUrl().getAddress();
                      // 倆個循環(huán),總共 replicaNumber 個虛擬節(jié)點,這樣做為了讓虛擬節(jié)點分布更均勻
                      for (int i = 0; i < replicaNumber / 4; i++) {
                          byte[] digest = Bytes.getMD5(address + i);
                          for (int h = 0; h < 4; h++) {
                              long m = hash(digest, h);
                              virtualInvokers.put(m, invoker);
                          }
                      }
                  }
              }
      
              public Invoker<T> select(Invocation invocation) {
                  boolean isGeneric = invocation.getMethodName().equals($INVOKE);
                  String key = toKey(invocation.getArguments(),isGeneric);
      
                  byte[] digest = Bytes.getMD5(key);
                  return selectForKey(hash(digest, 0));
              }
      
              private String toKey(Object[] args, boolean isGeneric) {
                  return isGeneric ? toKey((Object[]) args[1]) : toKey(args);
              }
      
              private String toKey(Object[] args) {
                  StringBuilder buf = new StringBuilder();
                  for (int i : argumentIndex) {
                      if (i >= 0 && args != null && i < args.length) {
                          buf.append(args[i]);
                      }
                  }
                  return buf.toString();
              }
      
              private Invoker<T> selectForKey(long hash) {
                  // 返回一個鍵大于等于給定鍵的最小鍵值對的Entry對象。如果沒有這樣的鍵值對存在,則返回null。
                  Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
                  if (entry == null) {
                      entry = virtualInvokers.firstEntry();
                  }
                  return entry.getValue();
              }
              
              // hash環(huán)2^32體現(xiàn)在這里,0xFFFFFFFFL = 2^32 - 1,說明 hash值只能取到0 ~ 2^32-1
              private long hash(byte[] digest, int number) {
                  return (((long) (digest[3 + number * 4] & 0xFF) << 24)
                          | ((long) (digest[2 + number * 4] & 0xFF) << 16)
                          | ((long) (digest[1 + number * 4] & 0xFF) << 8)
                          | (digest[number * 4] & 0xFF))
                          & 0xFFFFFFFFL;
              }
          }
      
      }
      
      

      3. 總結(jié)

      Hash算法: 用戶id % 節(jié)點數(shù),常被當作索引,用來快速定位數(shù)據(jù),但是在負載均衡這個問題上,存在容易導致數(shù)據(jù)傾斜、動態(tài)增減節(jié)點的問題。

      一致性Hash算法,通過將Hash環(huán)分為2^32個插槽,巧妙利用虛擬節(jié)點,提供了解決數(shù)據(jù)傾斜動態(tài)增減節(jié)點的思路。


      posted @ 2024-03-14 21:54  帥氣的濤啊  閱讀(583)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日本丰满少妇高潮呻吟| 亚洲一区二区精品极品| 乱人伦中文字幕成人网站在线 | 亚洲日本一区二区三区在线播放| 国产一级片内射在线视频| 五月综合网亚洲乱妇久久| 一本一本久久a久久综合精品| 人妻熟妇乱又伦精品无码专区| 久久这里只精品国产2| 日本无人区一区二区三区| 亚洲乱女色熟一区二区三区| 成人特黄特色毛片免费看| 无套内谢少妇毛片aaaa片免费| 国模肉肉视频一区二区三区| 久久久www成人免费精品| 亚洲熟妇熟女久久精品综合 | 亚洲伊人久久精品影院| 午夜通通国产精品福利| 国产精品久久无中文字幕| 成年女人片免费视频播放A| 亚洲精品国精品久久99热| 精品偷自拍另类精品在线| 一区二区视频| 国偷自产一区二区三区在线视频 | 最新国产精品中文字幕| 亚洲精品无amm毛片| 国产欧美在线一区二区三| 日韩精品中文字幕人妻| 黄色一级片一区二区三区| 美乳丰满人妻无码视频| 久久国产精品久久精品国产| 宅男噜噜噜66在线观看| 亚洲一卡2卡3卡4卡精品| 精品人妻伦一二二区久久| 九九re线精品视频在线观看视频| 国语精品国内自产视频| 日韩日韩日韩日韩日韩| 国产精品第一页中文字幕| 华人在线亚洲欧美精品| 亚洲国产成人无码影院| 亚洲精品综合网中文字幕|