一篇文章读懂Java哈希与一致性哈希算法

yizhihongxing

一篇文章读懂Java哈希与一致性哈希算法

1. 哈希算法基础

在计算机科学中,哈希算法是将任意长度的消息映射到固定长度的摘要 (或称哈希值) 的函数,也就是根据某种规则,将任意数据映射到指定大小范围的数值上,一般用于唯一性标识、数据校验等场景。

Java提供了多种哈希算法,比如MD5、SHA1、SHA256等,这些哈希算法的实现已经被封装在Java的类库中的java.security.MessageDigest里,我们可以通过MessageDigest类提供的API来实现数据的哈希值计算。

以下是一个Java中计算哈希值的示例代码:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HashExample {

    public static void main(String[] args) throws NoSuchAlgorithmException {
        String data = "hello world";
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        byte[] digest = messageDigest.digest(data.getBytes());
        String hash = bytesToHex(digest);
        System.out.println(hash);
    }

    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b & 0xff));
        }
        return sb.toString();
    }

}

上述代码使用了MD5哈希算法,对字符串"hello world"进行了哈希计算,并将哈希值以16进制形式输出。

2. 一致性哈希算法

2.1 基本概念

哈希算法的局限在于:哈希函数发生改变时,哈希到指定节点的映射关系随即会发生变化,这意味着在某一个节点离线或新加入一个节点时,数据将会受到极大的影响,因此需要一种能够保证性能同时具有高可用、负载均衡、动态扩容等特点的哈希算法——一致性哈希算法。

一致性哈希算法的基本思想是将整个哈希空间组织成一个虚拟的环,并按照顺时针方向映射到环上的位置。当加入一台新的机器时,根据这台机器的IP或者主机名计算出一个哈希值,并找到这个哈希值在环上的位置。然后将这台机器添加到这个位置之后,它就负责处理它和它后面所有节点中间负责的数据。

2.2 Java实现

Java中实现一致性哈希算法,主要依赖于SortedMap接口,它是一个有序的Map,可以通过key的哈希值来查找整个哈希环上的位置。

以下是一个简单的Java实现示例代码:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.SortedMap;
import java.util.TreeMap;

public class ConsistentHashExample {

    private SortedMap<Integer, String> circle = new TreeMap<>();

    private int hash(String key) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(key.getBytes());
            byte[] digest = messageDigest.digest();
            int hash = ((digest[3] & 0xff) << 24)
                    | ((digest[2] & 0xff) << 16)
                    | ((digest[1] & 0xff) << 8)
                    | (digest[0] & 0xff);
            return hash;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return 0;
        }
    }

    public void addNode(String node) {
        int hash = hash(node);
        circle.put(hash, node);
    }

    public void removeNode(String node) {
        int hash = hash(node);
        circle.remove(hash);
    }

    public String getNode(String key) {
        if (circle.isEmpty()) {
            return null;
        }
        int hash = hash(key);
        if (!circle.containsKey(hash)) {
            SortedMap<Integer, String> tailMap = circle.tailMap(hash);
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        return circle.get(hash);
    }

    public static void main(String[] args) {
        ConsistentHashExample example = new ConsistentHashExample();
        example.addNode("node1");
        example.addNode("node2");
        example.addNode("node3");
        System.out.println(example.getNode("key1"));
        System.out.println(example.getNode("key2"));
        example.removeNode("node2");
        System.out.println(example.getNode("key1"));
        System.out.println(example.getNode("key2"));
    }

}

上述代码实现了一致性哈希算法,并提供了添加节点、删除节点、查找节点的基本操作。在运行示例程序时,可以看到对于同一个key,不同时间的哈希值计算结果也不同,但是在物理节点变化时,查找key对应节点的结果很少发生变化。

3. 示例说明

以下是两个使用Java哈希算法和一致性哈希算法的示例。

3.1 基于哈希算法的缓存实现示例

在基于哈希算法的缓存实现中,我们可以使用哈希值来唯一标识一个对象在缓存中的位置。可以通过对象的哈希值来确定对象在缓存中的位置,并在缓存中查询该对象。

以下是一个基于哈希算法的缓存实现示例:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

public class Cache {

    private Map<String, Object> cache = new HashMap<>();

    private int hash(String key) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(key.getBytes());
            byte[] digest = messageDigest.digest();
            int hash = ((digest[3] & 0xff) << 24)
                    | ((digest[2] & 0xff) << 16)
                    | ((digest[1] & 0xff) << 8)
                    | (digest[0] & 0xff);
            return hash;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return 0;
        }
    }

    public void put(String key, Object value) {
        int hash = hash(key);
        cache.put(String.valueOf(hash), value);
    }

    public Object get(String key) {
        int hash = hash(key);
        return cache.get(String.valueOf(hash));
    }

    public static void main(String[] args) {
        Cache cache = new Cache();
        cache.put("key1", "value1");
        cache.put("key2", "value2");
        System.out.println(cache.get("key1"));
        System.out.println(cache.get("key2"));
    }

}

上述代码实现了一个缓存,使用MD5哈希算法计算对象的哈希值,从而实现缓存的查找和存储。

3.2 基于一致性哈希算法的负载均衡示例

在基于一致性哈希算法的负载均衡实现中,我们可以使用一致性哈希算法来选择工作节点。可以通过工作节点的ip地址或主机名计算哈希值,并根据哈希值的大小来选择工作节点。

以下是一个基于一致性哈希算法的负载均衡示例:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.SortedMap;
import java.util.TreeMap;

public class LoadBalancer {

    private SortedMap<Integer, String> circle = new TreeMap<>();

    private int hash(String key) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(key.getBytes());
            byte[] digest = messageDigest.digest();
            int hash = ((digest[3] & 0xff) << 24)
                    | ((digest[2] & 0xff) << 16)
                    | ((digest[1] & 0xff) << 8)
                    | (digest[0] & 0xff);
            return hash;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return 0;
        }
    }

    public void addNode(String node) {
        int hash = hash(node);
        circle.put(hash, node);
    }

    public void removeNode(String node) {
        int hash = hash(node);
        circle.remove(hash);
    }

    public String getNode(String key) {
        if (circle.isEmpty()) {
            return null;
        }
        int hash = hash(key);
        if (!circle.containsKey(hash)) {
            SortedMap<Integer, String> tailMap = circle.tailMap(hash);
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        return circle.get(hash);
    }

    public static void main(String[] args) {
        LoadBalancer lb = new LoadBalancer();
        lb.addNode("192.168.0.1");
        lb.addNode("192.168.0.2");
        lb.addNode("192.168.0.3");
        for (int i = 0; i < 10; i++) {
            String node = lb.getNode("key" + i);
            System.out.println("key" + i + " -> " + node);
        }
    }

}

上述代码实现了一个负载均衡器,使用一致性哈希算法实现工作节点的查找和选择,从而实现负载均衡的功能。在执行示例程序时,可以看到:随着节点数的增加,哈希槽的均匀性得到了保证,同时负载均衡的效果也得到了提升。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一篇文章读懂Java哈希与一致性哈希算法 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • MyBatis中的resultMap简要概述

    关于MyBatis中的resultMap,我将为您进行详细的讲解。首先,我们需要明确的是,ResultMap是MyBatis中非常重要的一个概念,它负责将ResultSet中的数据映射到JAVA对象中。在MyBatis中,我们既可以使用基于注解的方式,也可以使用XML文件来定义ResultMap。接下来,我们将从以下几个方面进行讲解: ResultMap是什…

    Java 2023年6月1日
    00
  • Java连接MySQL8.0 JDBC的详细步骤(IDEA版本)

    下面是使用IDEA连接MySQL8.0的详细步骤: 准备工作 安装MySQL 8.0 下载并安装Java 8或以上版本 下载MySQL的Java connector驱动程序(mysql-connector-java-{version}-bin.jar) 配置项目 在IDEA中创建一个新项目 在项目结构中添加MySQL connector驱动程序 在IDEA中…

    Java 2023年5月19日
    00
  • 基于centos自己构建一个tomcat镜像的实现

    要在CentOS上构建自己的Tomcat镜像,可以按照以下步骤: 步骤1:安装Docker Docker是一种容器化平台,我们需要使用它来构建我们的Tomcat镜像。在CentOS上安装Docker的方法可以参考Docker的官方文档。 步骤2:创建一个Dockerfile 在本地创建一个文件夹,用于存储Dockerfile和相关文件,例如: $ mkdir…

    Java 2023年5月19日
    00
  • Java的Struts框架报错“ForwardConfigException”的原因与解决办法

    当使用Java的Struts框架时,可能会遇到“ForwardConfigException”错误。这个错误通常由以下原因之一起: 无效的转发路径:如果转发路径无效,则可能会出现此错误。在这种情况下,需要检查转发路径以解决此问题。 无效的转发名称:如果转发名称无效,则可能会出现此错误。在这种情况下,需要检查转发名称以解决此问题。 以下是两个实例: 例 1 如…

    Java 2023年5月5日
    00
  • IntelliJ idea 如何生成动态的JSON字符串(步骤详解)

    下面是详细的攻略,包括两个示例说明。 IntelliJ idea 如何生成动态的JSON字符串(步骤详解) 一、使用Gson库生成JSON字符串 在IntelliJ Idea中创建一个Java项目,然后在项目中导入Gson库的jar包。 创建一个Java类,在类中定义一个类成员,用于存储需要生成的JSON数据。 “`java import com.goog…

    Java 2023年5月26日
    00
  • Java重写(Override)与重载(Overload)区别原理解析

    下面是详细讲解“Java重写(Override)与重载(Overload)区别原理解析”的攻略: Java重写(Override)与重载(Overload)区别原理解析 一、重写(Override) 1.1 定义 Java中,当子类继承父类时,如果子类需要覆盖(重写)父类中的方法,就需要使用重写。重写是指在子类中重新定义的方法覆盖在父类中定义的同名方法。 1…

    Java 2023年5月26日
    00
  • 将Tomcat Service化

    将Tomcat Service化是指将Tomcat服务器安装为系统服务,使其能够在系统启动时自动启动,而无需手动启动Tomcat。以下是将Tomcat Service化的完整攻略: 1. 下载和安装Tomcat 首先需要在官网上下载适合自己操作系统的Tomcat,并进行安装。 2. 配置JAVA环境变量 在系统环境变量中配置JAVA_HOME变量,使其指向J…

    Java 2023年6月15日
    00
  • 利用Java实现文件锁定功能

    接下来我将为你详细讲解如何利用Java实现文件锁定功能。 什么是文件锁定 文件锁定是指在对文件进行读取、修改等操作时,防止其他程序或者线程对同一文件进行操作,从而避免文件被多个程序同步修改而产生数据不一致的情况。 文件锁定的实现原理 文件锁定的实现原理是通过创建文件锁的方式来阻止其他程序访问被锁定的文件。在Java中,可以通过FileChannel类创建文件…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部