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

一篇文章读懂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日

相关文章

  • Java SimpleDateFormat与System类使用示例详解

    Java SimpleDateFormat与System类使用示例详解 SimpleDateFormat类 SimpleDateFormat是Java中用于格式化和解析日期的类,可以将日期转换为指定格式的字符串,也可以将指定格式的字符串转换为日期对象。 格式化日期 以下是一个将日期格式化为指定格式字符串的示例: import java.text.Simple…

    Java 2023年5月20日
    00
  • 浅谈java中unmodifiableList方法的应用场景

    浅谈Java中unmodifiableList方法的应用场景 在Java集合框架中,有一种叫做unmodifiableList的方法可以创建一个只读的List集合,即使尝试对该List进行写操作也会抛出UnsupportedOperationException异常。本篇文章将详细讲解unmodifiableList方法的应用场景。 1. 为何需要只读List…

    Java 2023年5月26日
    00
  • SpringMVC框架如何与Junit整合看这个就够了

    SpringMVC框架如何与Junit整合 本文将详细讲解如何使用Junit测试SpringMVC框架,并提供两个示例说明。 环境准备 在开始整合Junit和SpringMVC框架之前,我们需要准备以下环境: JDK 18或以上版本 Maven 3.6.3或以上版本 Tomcat 9.0或以上版本 Junit 5.7.2或以上版本 实现步骤 下面是整合Jun…

    Java 2023年5月17日
    00
  • JAVA字符串占位符使用方法实例

    JAVA字符串占位符使用方法实例 什么是字符串占位符 字符串占位符是在字符串中占有一定位置并留下标记,便于对应的变量填入字符串中,这在实际开发中十分常见。 在Java中,字符串占位符由一对大括号 {} 组成。 使用字符串占位符的语法 在Java中使用字符串占位符,可以通过 String.format() 方法来实现,语法如下: String.format(S…

    Java 2023年5月26日
    00
  • JSP简介

    JSP 简介 JSP(Java Server Pages)是一种动态的网页技术,它可以让开发人员将 Java 代码嵌入到 HTML 页面中。JSP 页面首先被翻译成 Java 代码,然后编译成 Servlet 类,最后将 Servlet 类加载到 Web 服务器中。当 Web 客户端请求 JSP 页面时,Web 服务器会处理该请求并返回 Servlet 的执…

    Java 2023年6月15日
    00
  • 初探Java内部类的使用

    初探Java内部类的使用 什么是内部类 Java中的内部类,指的是定义在另一个类中的类。内部类被认为是一个单独的实体,能够访问其外部类的所有成员。因此,内部类拥有更多的访问权限及更加灵活的控制能力。 一个内部类可以具有任意的访问权限及修饰符,这其中最为关键的是private,即表示该内部类仅仅只能被它的外部类所访问。不同的内部类也拥有不同的访问权限及特殊性质…

    Java 2023年5月26日
    00
  • Spring Boot 使用Druid详解

    Spring Boot使用Druid的详细攻略如下: 添加Druid依赖 在Spring Boot中使用Druid,需要在pom.xml文件中添加Druid的依赖: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot…

    Java 2023年5月15日
    00
  • 如何使用java制作假数据接口

    我们来详细讲解如何使用Java制作假数据接口的完整攻略。 什么是假数据接口 假数据接口是一种用于模拟真实数据的虚拟接口,通常用于在开发过程中替代实际接口进行测试、演示和展示。通过模拟数据,可以确保应用程序在与真实数据交互时能够正常工作,同时也可以在后端 API 开发尚未完成或测试环境不可用时进行前端开发。 如何使用Java制作假数据接口 在Java中,我们可…

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