java 哈希一致算法_一致哈希算法Java实现

一致哈希算法(Consistent Hashing Algorithms)是一个分布式系统中常用的算法。传统的Hash算法当槽位(Slot)增减时,面临所有数据重新部署的问题,而一致哈希算法确可以保证,只需要移动K/n份数据(K为数据总量, n为槽位数量),且只影响现有的其中一个槽位。这使得分布式系统中面对新增或者删除机器时,能够更快速的处理更改请求。本文将用Java实现一个简单版本的一致哈希算法,只为说明一致哈希算法的核心思想。

一致哈希算法介绍

一致哈希算法的介绍很多,如wiki,以及很多的博客。在此只简述其概念,详细的介绍请参考相关论文。第一个概念是节点(Node),分布式系统中相当于一台机器。所有的节点逻辑上围起来形成一个圆环。第二个概念是数据(Data),每份数据都有一个key值,数据总是需要存储到某一个节点上。数据和节点之间如何关联的呢?通过区域的概念关联,每一个节点都负责圆环上的一个区域,落在该区域上的就存储在该节点上,通常区域为左侧闭合,右侧开放的形式,如[2500,5000)。

以下是一个拥有4个节点的一致哈希算法示意图:

0818b9ca8b590ca3270a3433284dd417.png

总的范围定为10000,也限定了总槽位的数量。可以依照项目的需要,制定合适的大小。

Node1的起始位置为0,负责存储[0, 2500)之间的数据

Node2的起始位置为2500,负责存储[2500, 5000)之间的数据

Node3的起始位置为5000,负责存储[5000, 7500)之间的数据

Node4的起始位置为7500,负责存储[7500, 10000)之间的数据

一致哈希算法最大的特点在于新增或者删除节点的处理。如果新增一个起始位置为1250的节点Node5,那么影响到的唯一一个节点就是Node1,Node1的存储范围由[0, 2500)变更[0, 1250),Node5的存储范围为[1250, 2500),所以需要把落于[1250, 2500)范围的数据搬移到Node5上。其它的不需要做出改变,这一点非常的重要。相当于Node5分担了Node1的部分工作。如果把Node3删除,那么需要把Node3上面的数据搬移到Node2上面,Node2的范围扩大为[2500,7500),Node2承担了Node3的工作。

一致哈希算法Java的具体实现

Java是面向对象的语言,首先需要抽象对象。Node,表示节点,有名字,起始位置,以及数据列表三个属性,由于Node和数据之间的匹配,使用的是范围,所以为了简单起见,Node上加了一个end的属性。本来应该有Data以及DataKey的概念,但是为了简单起见,示例中Data就是字符串,Key就是自己。

整个圆环有一个长度,定义为scope,默认为10000。新增节点的算法是,找到最大的空挡,把新增节点放中间。当然也可以换为找到压力(数据量)最大的节点,把新增节点放在该节点之后。删除节点有一点小技巧,如果删除的是开始位置为0的节点,那么把下一个节点的开始位置置为0,和普通的退格不同。这能保证只要有节点,就一定有一个从0开始的节点。这能简化我们的算法和处理逻辑。

addItem方法就是往系统里面放数据,最后为了展示数据的分布效果,提供desc方法,打印出数据分布情况。很有意思。

整体代码如下:

public class ConsistentHash {

private int scope = 10000;

private List nodes;

public ConsistentHash() {

nodes = new ArrayList();

}

public int getScope() {

return scope;

}

public void setScope(int scope) {

this.scope = scope;

}

public void addNode(String nodeName) {

if (nodeName == null || nodeName.trim().equals("")) {

throw new IllegalArgumentException("name can't be null or empty");

}

if (containNodeName(nodeName)) {

throw new IllegalArgumentException("duplicate name");

}

Node node = new Node(nodeName);

if (nodes.size() == 0) {

node.setStart(0);

node.setEnd(scope);

nodes.add(node);

} else {

Node maxNode = getMaxSectionNode();

int middle = maxNode.start + (maxNode.end - maxNode.start) / 2;

node.start = middle;

node.end = maxNode.end;

int maxPosition = nodes.indexOf(maxNode);

nodes.add(maxPosition + 1, node);

maxNode.setEnd(middle);

// move data

Iterator iter = maxNode.datas.iterator();

while (iter.hasNext()) {

String data = iter.next();

int value = Math.abs(data.hashCode()) % scope;

if (value >= middle) {

iter.remove();

node.datas.add(data);

}

}

for (String data : maxNode.datas) {

int value = Math.abs(data.hashCode()) % scope;

if (value >= middle) {

maxNode.datas.remove(data);

node.datas.add(data);

}

}

}

}

public void removeNode(String nodeName) {

if (!containNodeName(nodeName)) {

throw new IllegalArgumentException("unknown name");

}

if (nodes.size() == 1 && nodes.get(0).datas.size() > 0) {

throw new IllegalArgumentException("last node, and still have data");

}

Node node = findNode(nodeName);

int position = nodes.indexOf(node);

if (position == 0) {

if (nodes.size() > 1) {

Node newFirstNode = nodes.get(1);

for (String data : node.datas) {

newFirstNode.datas.add(data);

}

newFirstNode.setStart(0);

}

} else {

Node lastNode = nodes.get(position - 1);

for (String data : node.datas) {

lastNode.datas.add(data);

}

lastNode.setEnd(node.end);

}

nodes.remove(position);

}

public void addItem(String item) {

if (item == null || item.trim().equals("")) {

throw new IllegalArgumentException("item can't be null or empty");

}

int value = Math.abs(item.hashCode()) % scope;

Node node = findNode(value);

node.datas.add(item);

}

public void desc() {

System.out.println("Status:");

for (Node node : nodes) {

System.out.println(node.name + ":(" + node.start + "," + node.end

+ "): " + listString(node.datas));

}

}

private String listString(LinkedList datas) {

StringBuffer buffer = new StringBuffer();

buffer.append("{");

Iterator iter = datas.iterator();

if (iter.hasNext()) {

buffer.append(iter.next());

}

while (iter.hasNext()) {

buffer.append(", " + iter.next());

}

buffer.append("}");

return buffer.toString();

}

private boolean containNodeName(String nodeName) {

if (nodes.isEmpty()) {

return false;

}

Iterator iter = nodes.iterator();

while (iter.hasNext()) {

Node node = iter.next();

if (node.name.equals(nodeName)) {

return true;

}

}

return false;

}

private Node findNode(int value) {

Iterator iter = nodes.iterator();

while (iter.hasNext()) {

Node node = iter.next();

if (value >= node.start && value < node.end) {

return node;

}

}

return null;

}

private Node findNode(String nodeName) {

Iterator iter = nodes.iterator();

while (iter.hasNext()) {

Node node = iter.next();

if (node.name.equals(nodeName)) {

return node;

}

}

return null;

}

private Node getMaxSectionNode() {

if (nodes.size() == 1) {

return nodes.get(0);

}

Iterator iter = nodes.iterator();

int maxSection = 0;

Node maxNode = null;

while (iter.hasNext()) {

Node node = iter.next();

int section = node.end - node.start;

if (section > maxSection) {

maxNode = node;

maxSection = section;

}

}

return maxNode;

}

static class Node {

private String name;

private int start;

private int end;

private LinkedList datas;

public Node(String name) {

this.name = name;

datas = new LinkedList();

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getStart() {

return start;

}

public void setStart(int start) {

this.start = start;

}

public int getEnd() {

return end;

}

public void setEnd(int end) {

this.end = end;

}

public LinkedList getDatas() {

return datas;

}

public void setDatas(LinkedList datas) {

this.datas = datas;

}

}

public static void main(String[] args) {

ConsistentHash hash = new ConsistentHash();

hash.addNode("Machine-1");

hash.addNode("Machine-2");

hash.addNode("Machine-3");

hash.addNode("Machine-4");

hash.addItem("Hello");

hash.addItem("hash");

hash.addItem("main");

hash.addItem("args");

hash.addItem("LinkedList");

hash.addItem("end");

hash.desc();

hash.removeNode("Machine-1");

hash.desc();

hash.addNode("Machine-5");

hash.desc();

hash.addItem("scheduling");

hash.addItem("queue");

hash.addItem("thumb");

hash.addItem("quantum");

hash.addItem("approaches");

hash.addItem("migration");

hash.addItem("null");

hash.addItem("feedback");

hash.addItem("ageing");

hash.addItem("bursts");

hash.addItem("shorter");

hash.desc();

hash.addNode("Machine-6");

hash.addNode("Machine-7");

hash.addNode("Machine-8");

hash.desc();

hash.addNode("Machine-9");

hash.addNode("Machine-10");

hash.addNode("Machine-11");

hash.desc();

hash.addNode("Machine-12");

hash.addNode("Machine-13");

hash.addNode("Machine-14");

hash.addNode("Machine-15");

hash.addNode("Machine-16");

hash.addNode("Machine-17");

hash.desc();

}

}

需要进一步完善的地方

不同节点之间互相备份,提高系统的可靠性。节点范围的动态调整,有时候分布可能不够平衡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java日志——基本日志+高级日志

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java日志——基本日志高级日志 的相关知识&#xff1b; 【1】记录日志 1.1&#xff09;出现的问题和解决方法&#xff1a; 1.1.1&#xff09;出现的问题&#xff1a;在我们调试…

java模式匹配_用Java匹配模式

java模式匹配如果您使用的是Java&#xff0c;那么您很有可能以前已经看过它的模式匹配。 String#matches(String)方法在内部使用Pattern类型&#xff0c;该类型包含更复杂的功能&#xff1a; 通过编译正则表达式来创建Pattern 。 该模式与任何输入字符串匹配&#xff0c;并且可…

java在frame怎么计时_java – 退出jframe时的Swing stop计时器

我有一个特定的任务,我按计划的时间间隔运行.基本上我在JFrame中的Label上显示相机.但是,当我退出JFrame时,应用程序似乎运行.我怎么能阻止它&#xff1f;我已经删除了代码的细节,只是将相关部分留在了public class TaskCLass extends JFrame {JPanel p;JLabel l;Timer timer;p…

java日志——修改日志管理器配置+日志本地化

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java日志——修改日志管理器配置日志本地化 的相关知识&#xff1b; 【1】修改日志管理器配置 1.1&#xff09;可以通过编辑配置文件来修改日志系统的各种属性。 在默认情况下&a…

jpa embedded_JPA @Embeddable和@Embedded

jpa embedded介绍&#xff1a; 作为一个面向对象的开发人员&#xff0c;我们希望避免使用带有大量不相关字段的大型类。 因此&#xff0c;我们可能经常感到有必要使用多个对象来表示JPA实体。 在本快速教程中&#xff0c;我们将学习如何使用JPA或Hibernate中的Embedded和Embed…

jdk内置线程实例_EA问题的JDK14实例

jdk内置线程实例Tagir Valeev最近发布了一条有关即将发布的Java JDK14版本的预览功能的推文&#xff1a; &#xff03;Java14模式匹配将名称隐藏带入了更高的疯狂程度。 在这里&#xff0c;我为FLAG字段添加或删除了final修饰符&#xff0c;该修饰符仅在不可达的if分支中访问。…

intro to Apache Log4j 2

【0】README 0.1&#xff09;本文作为 原文&#xff08;http://logging.apache.org/log4j/2.x/&#xff09;的译文&#xff0c;仅作参考&#xff0c; 旨在了解 Log4j 2 的相关知识 &#xff1b; 0.2&#xff09; Apache Log4j 2 是Log4j的升级版&#xff0c;并对其前任Log4j …

php注册页面模板,选项卡式WordPress登陆注册模板

上次说到不用插件也可以修改WordPress登陆注册界面&#xff0c;不过只是简单的修改了CSS&#xff0c;缺少很多元素&#xff0c;例如header、导航、footer等&#xff0c;为了增加这些元素&#xff0c;我们可以采取另一种方式&#xff0c;将WordPress登陆注册界面做成page模板&am…

内核堆栈 用户堆栈_弹性堆栈介绍

内核堆栈 用户堆栈当您运行对公司至关重要的软件时&#xff0c;您将无法拥有仅用于分析一段时间前发生的事情的日志&#xff0c;让客户端告诉您您的应用程序已损坏&#xff0c;而您甚至不知道发生了什么是真实的问题。 解决该问题的方法之一是使用监视和日志记录。 大多数应用…

php 字符串 中文,php 中文字符串截取乱码

PHP截取字符串如果是英文直接用substr就可以了,但对于中文字符&#xff0c;用substring可能会导致乱码&#xff0c;那么将如何解决呢&#xff1f;1、通过函数mb_substr实现说明&#xff1a;mb_substr($str, $start, $length, $encoding);通过该函数即可&#xff0c;但需要加载p…

java 调试技巧

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java 调试技巧 的相关知识&#xff1b; 【1】调试技巧相关 1.1&#xff09;可以用下面的方法打印或记录任意变量的值&#xff1a; System.out.println("x " x); 或 …

jdk 版本和内部版本对应_JDK 14 Rampdown:内部版本27

jdk 版本和内部版本对应马克 雷因霍尔德&#xff08; Mark Reinhold&#xff09;最近的帖子“ JDK 14现在处于Rampdown第一阶段 ”宣布“我们现在处于Rampdown第一阶段”&#xff0c;并且“整体功能已冻结”。 JDK 14 Early Access Build &#xff03;27&#xff08;2019/12/12…

对一个java源文件进行正确编译,给定如下一个Java源文件Child.java,编译并运行Child.java,以下结果正确的是()...

总成化无本优部问决企题业内法解&#xff0c;给定流配特别题是物送问。而他再告真的不是己这却一诉自&#xff0c;个源文译并运行下某人重病的家得了假如人忽然间&#xff0c;的应对策略有他使用到。结果感是个人自我指一效能对&#xff1a;。哲学的规揭示律一般&#xff0c;管…

异常java.lang.Thread.dumpStack(Unknown Source)

转自&#xff1a; http://www.blogjava.net/landon/archive/2011/02/27/345265.html 昨天在公司写了一段代码&#xff0c;很简单&#xff0c;就是测试Thread的dumpStack方法的使用。 因为Thread的dumpStack方法不是很常用&#xff0c;但它对于如果想看看谁在运行时调用方法还是…

java集合框架——接口图+类图+遗留类图

【0】README 0.1&#xff09;绝对的干货&#xff0c;理清 java集合框架中类和接口的层次关系&#xff1b;

构建maven项目插件_如何构建一个Maven插件

构建maven项目插件使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证&#xff0c;管理和保护。 今天尝试Okta。 由于其插件生态系统的普及&#xff0c;Apache Maven仍然是Java领域最受欢迎的构建工具。 很容易找到…

shu函数php,【函数分享】每日PHP函数分享(2021-3-3)

array_intersect_assoc — 带索引检查计算数组的交集说明array_intersect_assoc ( array $array1 , array $array2 , array $... ? ) : arrayarray_intersect_assoc() 返回一个数组&#xff0c;该数组包含了所有在 array1 中也同时出现在所有其它参数数组中的值。注意和 arra…

spring 注释_Spring@主要注释

spring 注释介绍&#xff1a; 当存在多个相同类型的bean时&#xff0c;使用Spring Primary批注为标记的bean提供更高的优先级。 默认情况下&#xff0c;Spring按类型自动连线。 因此&#xff0c;当Spring尝试自动装配并且有多个相同类型的bean时&#xff0c;我们将得到一个NoU…

java集合——集合接口+迭代器接口

【0】README 0.1&#xff09; 本文描述转自 core java volume 1&#xff0c; 源代码 diy 的&#xff0c; 旨在理解 java集合框架——集合接口迭代器接口 的相关知识&#xff1b; 0.2&#xff09; for full source code , please visit https://github.com/pacosonTang/core-j…

snmp在php中的使用,在php中转换python代码以计算snmpvlan掩码的最佳方法

我有一些python代码&#xff0c;我想在一个100%的php代码中使用。你知道我怎么转换代码吗&#xff1f;&#xff1f;&#xff1f;我在转换代码时遇到问题&#xff0c;尤其是部分get bit和set bit。在位掩码通过snmp从交换机中读取&#xff0c;掩码表示交换机端口位于定义的vlan中…