当前位置: 首页 > news >正文

一致性hash原理与实现

为什么80%的码农都做不了架构师?>>>   hot3.png

一致性hash算法原理:

23110236_qEUY.gif

把数据用hash函数(如MD5),映射到一个很大的空间里,如图所示。数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如k1对应到了图中所示的位置,然后沿顺时针找到一个机器节点B,将k1存储到B这个节点中。

如果B节点宕机了,则B上的数据就会落到C节点上,如下图所示:

23110236_Lf9u.gif

这样,只会影响C节点,对其他的节点A,D的数据不会造成影响。然而,这又会造成一个“雪崩”的情况,即C节点由于承担了B节点的数据,所以C节点的负载会变高,C节点很容易也宕机,这样依次下去,这样造成整个集群都挂了。

       为此,引入了“虚拟节点”的概念:即把想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点,如下图所使用:

23110237_us86.gif

图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。

应用举例:

    1、在memcache集群中使用一致性hash算法可以任意添加和删除服务节点,使其只有一个节点受影响,而且可以将缓存平均的存在各个节点上,不会导致雪崩现象。

    2、在提供socket服务集群中有多台服务器,客户端在建立连接时可以使用一致性hash来选择一个服务器进行链接,可以有效地平衡各个服务器的压力。

代码实现:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;


public class ConsistentHash<T> {

    private final int numberOfReplicas;
    private volatile TreeMap<Integer, List<T>> circle = new TreeMap<>();

    private static final int circleSize = 188833;

    /***
     * 
     * @param numberOfReplicas
     * 	每个节点的虚拟节点的个数
     * @param nodes
     */
    public ConsistentHash(int numberOfReplicas, Collection<T> nodes) {
        this.numberOfReplicas = numberOfReplicas;
        for (T node : nodes) {
            addNode(circle, node);
        }
    }

    public synchronized void add(T node) {
        TreeMap<Integer, List<T>> newCircle = copyCircle();
        addNode(newCircle, node);
        this.circle = newCircle;
    }

    public synchronized void remove(T node)	{
        TreeMap<Integer, List<T>> newCircle = copyCircle();
        remove(newCircle, node);
        this.circle = newCircle;
    }

    private TreeMap<Integer, List<T>> copyCircle() {
        TreeMap<Integer, List<T>> newTree = new TreeMap<>();

        for (Map.Entry<Integer, List<T>> entry : circle.entrySet())	{
            List<T> list = new ArrayList<T>();
            list.addAll(entry.getValue());
            newTree.put(entry.getKey(), list);
        }
        return newTree;
    }

    private void addNode(TreeMap<Integer, List<T>> circle, T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            int key = hashMd5(node.toString() + i);
            List<T> list = circle.get(key);
            if (list == null) {
                list = new ArrayList<T>();
                circle.put(key, list);
            }
            if (!containsNode(list, node)) {
                list.add(node);
            }
        }
    }

    private void removeNodeToList(List<T> list, T node)	{
        Iterator<T> it = list.iterator();
        while (it.hasNext()) {
            if (node.equals(it.next())) {
                it.remove();
            }
        }
    }

    private boolean containsNode(List<T> list, T node) {
        for (T t : list) {
            if (t.equals(node))	{
                return true;
            }
        }
        return false;
    }
    
    private void remove(TreeMap<Integer, List<T>> circle, T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            int key = hashMd5(node.toString() + i);
            List<T> list = circle.get(key);
            if (list != null) {
                if (list.contains(node)) {
                    removeNodeToList(list, node);
                }
                if (list.isEmpty())	{
                    circle.remove(key);
                }
            }
        }
    }

    public T get(Object key) {
        if (circle.isEmpty()) {
            return null;
        }
        int hash = hashMd5(key);
        Map.Entry<Integer, List<T>> entry = circle.ceilingEntry(hash);    //返回键值对,该键至少大于或等于给定键,如果不存在这样的键的键 - 值映射,则返回null相关联。
        List<T> node = null;
        if (entry == null) {
            node = circle.firstEntry().getValue();
        }
        else {
            node = entry.getValue();
        }
        if (node != null && !node.isEmpty()) {
            return node.get(0);
        }
        return null;
    }

    private static int hashCode(byte[] bytes) {
        int hash = 0;
        for (byte b : bytes) {
            hash = hash * 31 + ((int) b & 0xFF);
            if (hash > 0x4000000) {
                hash = hash % 0x4000000;
            }
        }
        return hash;
    }

    private  int hashMd5(Object o) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(o.toString().getBytes());
            int hashCode = hashCode(bytes);
            return hashCode % circleSize;
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return 0;
    }
}



转载于:https://my.oschina.net/hnrpf/blog/645837

相关文章:

  • 作为一个程序员需要了解多少网络方面的基础?网络基础总结(不断更新)
  • 四千字从源码分析ConcurrentHashMap的底层原理(JDK1.8)
  • redis学习笔记6--集合类型
  • 都2020年了,你还不知道count(1)和count(*)谁效率更高吗?
  • Linux下PF_PACKET的使用,RARP的server和client程序
  • 面试官:不会真有人不知道什么是线程池吧?
  • 从零搭建基于SpringBoot的秒杀系统(一):项目准备
  • 【总结】oracle恢复误删除数据,解除锁定的等sql语句
  • 从零搭建基于SpringBoot的秒杀系统(二):快速搭建一个SpringBoot项目
  • 重拾cgi——cgi dispatcher
  • 从零搭建基于SpringBoot的秒杀系统(三):首页、详情页编写
  • 从零搭建基于SpringBoot的秒杀系统(四):雪花算法生成订单号以及抢购功能实现
  • 操作系统实验一 命令解释程序的编写
  • 从零搭建基于SpringBoot的秒杀系统(五):基于Shiro的人员登陆认证
  • 从零搭建基于SpringBoot的秒杀系统(六):使用RabbitMQ让订单指定时间后失效
  • 收藏网友的 源程序下载网
  • 30天自制操作系统-2
  • angular组件开发
  • JS+CSS实现数字滚动
  • Less 日常用法
  • Linux快速复制或删除大量小文件
  • Linux中的硬链接与软链接
  • Rancher-k8s加速安装文档
  • React Native移动开发实战-3-实现页面间的数据传递
  • Travix是如何部署应用程序到Kubernetes上的
  • 闭包,sync使用细节
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 如何进阶一名有竞争力的程序员?
  • 微信小程序开发问题汇总
  • 一道闭包题引发的思考
  • const的用法,特别是用在函数前面与后面的区别
  • 函数计算新功能-----支持C#函数
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • $refs 、$nextTic、动态组件、name的使用
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (c语言)strcpy函数用法
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .htaccess配置重写url引擎
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • @Data注解的作用
  • @RequestMapping-占位符映射
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [20180129]bash显示path环境变量.txt
  • [bzoj2957]楼房重建
  • [C++]拼图游戏
  • [EFI]Dell Latitude-7400电脑 Hackintosh 黑苹果efi引导文件
  • [Go WebSocket] 多房间的聊天室(三)自动清理无人房间
  • [IE编程] 如何编程清除IE缓存