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

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

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

      redis一致性Hash,

       

      在微服務領域,使用Redis做緩存可并不是一件容易的事情。

      像新浪、推特這樣的應用,許許多多的熱點數據全都存放在Redis這一層,打到DB層的請求并不多,可以說非常依賴緩存了。如果緩存掛掉,流量全部穿透到DB層,其必然不堪其重,整個系統也會隨之癱瘓,后果非常嚴重。
      由于緩存數據量很大,Redis快正是快在其基于內存的快速存取,而計算機的內存資源又是十分有限的,故分布式緩存集群面臨著伸縮性的要求。

      問題就在這時出現了,所有的緩存數據是分散存放在各個Redis節點上的,通過客戶端實現路由算法,來將某個key路由到某個具體的節點。
      這個路由算法是分布式緩存伸縮性能否成功的關鍵。
      它的職責不僅僅是由key算出一個Redis的地址,而且必須讓新上線的緩存服務器對整個分布式緩存集群影響最小,使得擴容后,整個緩存服務器集群中已經緩存的數據盡可能還被訪問到。

      這里可以舉一個例子,比如用取余數(hash(key)%serverNum)做為該算法,Redis需要由3個節點,擴大到4個節點,會有75%的key無法命中,如下圖:

      hash(key)hash(key)/3hash(key)/4是否命中
      1 1 1
      2 2 2
      3 0 3
      4 1 0
      5 2 1
      6 0 2
      7 1 3
      8 2 0
      9 0 1
      10 1 2
      11 2 3
      12 0 0

      這種效果非常糟糕,當服務器數量為100臺時,再增加一臺新服務器,不能命中率將達到99%,這和整個緩存服務掛了一個效果。

      而一致性Hash正是為了解決這個問題而出現的,該路由算法通過引入一個一致性Hash環,以及進一步增加虛擬節點層,來實現盡可能高的命中率。
      關于該算法的具體原理與網上已經有一些說得很透徹的文章,本文不再贅述。


      本機部署多個Redis節點

      要對一致性Hash進行驗證,要做好準備工作,最直接地,首先要有一個Redis集群。這里我通過使用在本機上部署多個Redis實例指向不同端口來模擬這一形態。

      建立項目目錄:$ mkdir redis-conf
      之后將redis的配置copy一份過來并復制為5份,分別命名為redis-6379.conf~redis-6383.conf。

      需要對其內容進行一些修改才能正常啟動,分別找到配置文件中的如下兩行并對數字進行相應修改。

      port 6379
      pidfile /var/run/redis_6379.pid
      

      然后就可以分別啟動了:redis-server ./redis-6379 &
      可以使用redis-cli -p 6379來指定連接的redis-server。
      不妨進行一次嘗試,比如在6379設置key 1 2,而到6380 get 1只能得到nil,說明它們是各自工作的,已經滿足可以測試的條件。

       
      不同的節點展示

       

      代碼實現

      先說一下思路。
      部署4個節點,從6379到6382,通過一致性Hash算法,將key: 0~99999共100000個key分別set到這4個服務器上,然后再部署一個節點6383,這時再從0到99999開始get一遍,統計get到的次數來驗證命中率是否為期望的80%(4/5)。

      一致性Hash算法的實現嚴重借鑒了這篇文章,使用紅黑樹來做數據結構,來實現log(n)的查找時間復雜度,使用FNV1_32_HASH哈希算法來盡可能使key與節點分布得更加均勻,引入了虛擬節點,來做負載均衡。
      建議讀者詳細看下這篇文章,里面的講解非常詳細易懂。

      下面是我改寫過后的代碼:

      package org.guerbai.io.jedistry;
      
      import redis.clients.jedis.Jedis;
      import java.util.*;
      
      class JedisProxy {
      
         private static String[][] redisNodeList = {
                 {"localhost", "6379"},
                 {"localhost", "6380"},
                 {"localhost", "6381"},
                 {"localhost", "6382"},
         };
      
         private static Map<String, Jedis> serverConnectMap = new HashMap<>();
      
         private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();
      
         private static final int VIRTUAL_NODES = 100;
      
         static
         {
             for (String[] str: redisNodeList)
             {
                 addServer(str[0], str[1]);
             }
             System.out.println();
         }
      
         private static int getHash(String str)
         {
             final int p = 16777619;
             int hash = (int)2166136261L;
             for (int i = 0; i < str.length(); i++)
                 hash = (hash ^ str.charAt(i)) * p;
             hash += hash << 13;
             hash ^= hash >> 7;
             hash += hash << 3;
             hash ^= hash >> 17;
             hash += hash << 5;
      
             // 如果算出來的值為負數則取其絕對值
             if (hash < 0)
                 hash = Math.abs(hash);
             return hash;
         }
      
         private static String getServer(String node)
         {
             // 得到帶路由的結點的Hash值
             int hash = getHash(node);
             // 得到大于該Hash值的所有Map
             SortedMap<Integer, String> subMap =
                     virtualNodes.tailMap(hash);
             // 第一個Key就是順時針過去離node最近的那個結點
             if (subMap.isEmpty()) {
                 subMap = virtualNodes.tailMap(0);
             }
             Integer i = subMap.firstKey();
             // 返回對應的虛擬節點名稱,這里字符串稍微截取一下
             String virtualNode = subMap.get(i);
             return virtualNode.substring(0, virtualNode.indexOf("&&"));
         }
      
         public static void addServer(String ip, String port) {
             for (int i = 0; i < VIRTUAL_NODES; i++)
             {
                 String virtualNodeName = ip + ":" + port + "&&VN" + String.valueOf(i);
                 int hash = getHash(virtualNodeName);
                 System.out.println("虛擬節點[" + virtualNodeName + "]被添加, hash值為" + hash);
                 virtualNodes.put(hash, virtualNodeName);
             }
             serverConnectMap.put(ip+":"+port, new Jedis(ip, Integer.parseInt(port)));
         }
      
         public String get(String key) {
             String server = getServer(key);
             Jedis serverConnector = serverConnectMap.get(server);
             if (serverConnector.get(key) == null) {
                 System.out.println(key + "not in host: " + server);
             }
             return serverConnector.get(key);
         }
      
         public void set(String key, String value) {
             String server = getServer(key);
             Jedis serverConnector = serverConnectMap.get(server);
             serverConnector.set(key, value);
             System.out.println("set " + key + " into host: " + server);
         }
      
         public void flushdb() {
             for (String str: serverConnectMap.keySet()) {
                 System.out.println("清空host: " + str);
                 serverConnectMap.get(str).flushDB();
             }
         }
      
         public float targetPercent(List<String> keyList) {
             int mingzhong = 0;
             for (String key: keyList) {
                 String server = getServer(key);
                 Jedis serverConnector = serverConnectMap.get(server);
                 if (serverConnector.get(key) != null) {
                     mingzhong++;
                 }
             }
             return (float) mingzhong / keyList.size();
         }
      
      }
      
      public class ConsistencyHashDemo {
      
         public static void main(String[] args) {
             JedisProxy jedis = new JedisProxy();
             jedis.flushdb();
             List<String> keyList = new ArrayList<>();
             for (int i=0; i<100000; i++) {
                 keyList.add(Integer.toString(i));
                 jedis.set(Integer.toString(i), "value");
             }
             System.out.println("target percent before add a server node: " + jedis.targetPercent(keyList));
             JedisProxy.addServer("localhost", "6383");
             System.out.println("target percent after add a server node: " + jedis.targetPercent(keyList));
         }
      }
      

      首先,他的getServer方法會有些問題,當key大于最大的虛擬節點hash值時tailMap方法會返回空,找不到節點會報錯,其實這時應該去找hash值最小的一個虛擬節點。我加了處理,把這個環連上了。
      getHash方法為FNV1_32_HASH算法,可以不用太在意。
      VIRTUAL_NODES的值比較重要,當節點數目較少時,虛擬節點數目越大,命中率越高。

      在程序設計上也有很大的不同,我寫了JedisProxy類,來做為client訪問Redis的中間層,在該類的static塊中利用服務器節點生成虛擬節點構造好紅黑樹,getServer里根據tailMap方法取出實際節點的地址,再由實際節點的地址直接拿到jedis對象,提供簡單的get與set方法,先根據key拿特定的jedis對象,再進行get, set操作。

      addServer靜態方法給了其動態擴容的能力,可以看到在main方法中,通過調用JedisProxy.addServer("localhost", "6383")便直接增加了節點,不用停應用。
      targetPercent方法是用來統計命中率用。

      當虛擬節點為5時,命中率約為60%左右,把它加大到100后,可以到達預期的80%的命中率。

       

      posted @ 2021-01-13 15:52  w'c's  閱讀(140)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产另类久久久精品| 亚洲国产精品高清久久久 | 99国产欧美另类久久久精品| 精品无码一区在线观看| 亚洲人妻系列中文字幕| 国产精品亚洲mnbav网站| 性欧美vr高清极品| 库尔勒市| 国产精品综合一区二区三区 | 亚洲永久一区二区三区在线| 免费人成视频网站在线观看18| 亚洲制服无码一区二区三区| 国内精品自产拍在线播放| 亚洲国产成熟视频在线多多| 成人看的污污超级黄网站免费| 欧洲lv尺码大精品久久久| 亚洲精品国产一区二区三| 国产成人一区二区三区免费| 亚洲AV日韩AV永久无码电影| 精品无人区一区二区三区在线| 伊人久久大香线蕉av一区二区| 国产一区二区av天堂热| 国产av中文字幕精品| 3d无码纯肉动漫在线观看| 国产精品嫩草99av在线| 国产999久久高清免费观看| 亚洲一区在线观看青青蜜臀| 久久96热在精品国产高清| 亚洲精品一二三四区| 欧美高清狂热视频60一70| 精品不卡一区二区三区| 午夜福利yw在线观看2020| 久99久热只有精品国产99| 国产精品v片在线观看不卡| 国产成人理论在线视频观看| 国产精品v片在线观看不卡| 蜜桃av无码免费看永久| 又爽又黄又无遮挡的视频| 亚洲自拍偷拍福利小视频| 中年国产丰满熟女乱子正在播放| 精品国产片一区二区三区|