zoukankan      html  css  js  c++  java
  • 一致性哈希环的理论实现

    前言


    最近阅读社区代码时,发现了一段富有创造性的程序算法–一致性哈希环。也就是一致性哈希算法的具体实现,由一位微软工程师在提交社区代码时,笔者review到的,感觉代码实现严谨简洁,并且把一致性哈希环的特点全考虑到了,是一段很不错的算法程序。本文简单对其进行分析,解释。一致性哈希算法这里就不多介绍了,可点击笔者之前写过的文章一致性哈希算法。一致性哈希算法在分布式系统中有很多的应用场景,主要是为了解决数据出现“热点”问题。目前这段算法是用于为待写入数据选择目标集群位置的,目标集群会有很多个,而写入的文件数据只能选择其中1个集群。

    一致性哈希环算法实现

    /**
     * Consistent hash ring to distribute items across nodes (locations). If we add
     * or remove nodes, it minimizes the item migration.
     * 一致性哈希环,分散化实体项的节点位置选择,减少因为节点的变更导致的其上所属实体项的迁移。
     */
    public class ConsistentHashRing {
      private static final String SEPERATOR = "/";
      private static final String VIRTUAL_NODE_FORMAT = "%s" + SEPERATOR + "%d";
    
      /** Hash ring 哈希环. */
      private SortedMap<String, String> ring = new TreeMap<String, String>();
      /** 虚拟节点信息 -> 节点数 映射信息 */
      /** Entry -> num virtual nodes on ring. */
      private Map<String, Integer> entryToVirtualNodes =
          new HashMap<String, Integer>();
    
      /** Synchronization 锁同步. */
      private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
      private final Lock readLock = readWriteLock.readLock();
      private final Lock writeLock = readWriteLock.writeLock();
    
      public ConsistentHashRing(Set<String> locations) {
        for (String location : locations) {
          // 在环内添加位置信息
          addLocation(location);
        }
      }
    
      /**
       * Add entry to consistent hash ring.
       * 添加实体项到哈希环内
       * @param location Node to add to the ring.
       */
      public void addLocation(String location) {
        // 虚拟出100个节点插入
        addLocation(location, 100);
      }
    
      /**
       * Add entry to consistent hash ring.
       * 添加具体项到哈希环内
       * @param location 需要添加的节点.
       * @param numVirtualNodes 需要添加的虚拟节点数。
       */
      public void addLocation(String location, int numVirtualNodes) {
        writeLock.lock();
        try {
          // 更新虚拟节点列表信息
          entryToVirtualNodes.put(location, numVirtualNodes);
          for (int i = 0; i < numVirtualNodes; i++) {
            // 得到虚拟节点名
            String key = String.format(VIRTUAL_NODE_FORMAT, location, i);
            // 取其哈希值
            String hash = getHash(key);
            // 加入到哈希环内
            ring.put(hash, key);
          }
        } finally {
          writeLock.unlock();
        }
      }
    
      /**
       * Remove specified entry from hash ring.
       * 从哈希环内移除实体项
       * @param location Node to remove from the ring.
       */
      public void removeLocation(String location) {
        writeLock.lock();
        try {
          // 移除给定节点位置,并获取其对应的虚拟节点数
          Integer numVirtualNodes = entryToVirtualNodes.remove(location);
          for (int i = 0; i < numVirtualNodes; i++) {
            // 得到虚拟节点key,并从哈希环内移除
            String key = String.format(VIRTUAL_NODE_FORMAT, location, i);
            String hash = getHash(key);
            ring.remove(hash);
          }
        } finally {
          writeLock.unlock();
        }
      }
    
      /**
       * Return location (owner) of specified item. Owner is the next
       * entry on the hash ring (with a hash value > hash value of item).
       * 从哈希环内去得其最近的节点位置
       * @param item Item to look for.
       * @return The location of the item.
       */
      public String getLocation(String item) {
        readLock.lock();
        try {
          if (ring.isEmpty()) {
            return null;
          }
          // 计算输入路径的哈希值
          String hash = getHash(item);
          // 如果哈希环内不恰好包含此节点
          if (!ring.containsKey(hash)) {
            // 将哈希环定位到大于此key的首个位置
            SortedMap<String, String> tailMap = ring.tailMap(hash);
            // 并得到第一个大于此key的项目的key,也就是距离最近的key
            hash = tailMap.isEmpty() ? ring.firstKey() : tailMap.firstKey();
          }
          // 根据此key得到对应的虚拟节点信息
          String virtualNode = ring.get(hash);
          // 然后从虚拟节点信息中得到实际位置信息
          int index = virtualNode.lastIndexOf(SEPERATOR);
          if (index >= 0) {
            return virtualNode.substring(0, index);
          } else {
            return virtualNode;
          }
        } finally {
          readLock.unlock();
        }
      }
    
      public String getHash(String key) {
        return MD5Hash.digest(key).toString();
      }
    
      /**
       * Get the locations in the ring.
       * @return Set of locations in the ring.
       */
      public Set<String> getLocations() {
        return entryToVirtualNodes.keySet();
      }
    }
  • 相关阅读:
    周末毒鸡汤时间
    MySQL 8.0发布,你熟悉又陌生的Hash Join?
    你可能需要的Kafka面试题与答案整理
    流程控制结构
    视图
    事务
    常用约束
    sql99语法的连接查询
    数据类型
    数据操作语句(DML)
  • 原文地址:https://www.cnblogs.com/bianqi/p/12183636.html
Copyright © 2011-2022 走看看