一篇文章读懂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中实现链式操作(方法链)的简单例子

    当我们在Java中调用一个对象的方法时,我们通常会按照顺序调用每个方法。但是,有时候我们的调用链非常长,这导致代码变得冗长和难以阅读。为了解决这个问题,我们可以使用链式操作(方法链)。 链式操作是一种通过链接多个方法使代码更简洁易读的技术。使用这种技术,我们可以在单行代码中执行多个方法。在本文中,我们将向您展示如何在Java中实现这种方法链的技术。 什么是链…

    Java 2023年5月18日
    00
  • JSP页面pageEncoding和contentType属性

    JSP(JavaServer Pages)是一种动态Web编程技术,用于在Web服务器中生成动态网页。在JSP中,pageEncoding和contentType都是非常重要的属性。下面我们将逐步介绍这两个属性。 pageEncoding属性 pageEncoding属性用于指定JSP文件的字符编码。在JSP中,如果没有指定编码类型,那么默认编码类型将是IS…

    Java 2023年6月15日
    00
  • Spring boot异步任务原理全面分析

    Spring Boot异步任务原理全面分析 在Spring Boot中,我们经常需要执行一些耗时的操作,如果将它们放入主线程中进行,会导致响应变慢,用户体验不佳。而异步任务可以避免这种情况的出现。 什么是Spring Boot异步任务 Spring Boot异步任务是指在独立的线程中处理某些任务,将主线程从处理任务中解放出来的机制。Spring Boot提供…

    Java 2023年5月19日
    00
  • 详解Java中的File文件类以及FileDescriptor文件描述类

    详解Java中的File文件类以及FileDescriptor文件描述类 1. File文件类 1.1 什么是File文件类 Java中的File类用于表示文件或目录的路径名,是访问文件系统中的文件或目录的主要类。通过File类,可以创建、删除、重命名文件或目录,或访问文件或目录的各种属性。 1.2 File类的构造方法 File(String path):…

    Java 2023年5月20日
    00
  • ASP中Session技巧 默认过期时间为20分钟

    ASP中的Session技巧是网站开发中常用的技术,通过使用Session,我们可以在不同的页面间共享数据和信息。在ASP中,Session的默认过期时间为20分钟,为了更好地利用Session技术并确保其正常运行,我们需要注意以下几点: 设置Session过期时间 为了避免Session失效,我们可以通过设置Session过期时间来保持Session的有效…

    Java 2023年6月15日
    00
  • Spring装配Bean之用Java代码安装配置bean详解

    下面我将详细讲解使用Java代码进行Spring Bean的装配配置的完整攻略。 1. 概述 Spring框架的一个重要特点就是使得Bean配置非常灵活。在Spring中,我们可以用XML、Java注解或者纯Java代码等多种方式来实现对Bean的装配配置。其中,使用Java代码的方式可以减少XML配置文件的复杂度,同时也可以提高程序的可读性和灵活性。 2.…

    Java 2023年6月15日
    00
  • Java中的运算符重载是什么?

    Java中的运算符重载是指允许在自定义的类中对运算符(如+、-、*、/等)进行重新定义,以便对自定义的类进行运算。运算符重载的本质是将运算符号的含义进行扩展,使得一种运算符号能够被用于多种类型的数据操作。 运算符重载是实现多态性的一个重要技巧。对于类中的不同对象,运算符的行为可以有所不同,这样可以减少代码的冗余,提高代码的复用性。 运算符重载实现起来比较简单…

    Java 2023年4月27日
    00
  • Java中日期格式化YYYY-DD的操作bug

    首先需要明确一点,关于Java日期格式化中YYYY和yyyy的区别。YYYY是基于周的年份,而yyyy是基于实际年份。 假设我们有以下的日期字符串:2021-08-01。如果使用如下的格式化模式:YYYY-DD,希望得到的结果是2021-01。但是实际输出的结果是2020-01。这是由于Java的日期格式化器在处理模式字符串时,YYYY会被认为是“基于周的年…

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