一致性Hash热点

一致性Hash算法是来解决热点问题,如果虚拟节点设置过小热点问题仍旧存在。

关于一致性Hash算法的原理我就不说了,网上有很多人提供自己编写的一致性Hash算法的代码示例,我在跑网上的代码示例发现还是有热点问题。为此我翻阅了Jedis的ShardedJedis类的源码把它的一致性Hash算法提取出来,作为自己的一个工具类,以后自己工程开发中用起来也放心些,毕竟jedis的代码经受了大家的验证。

提取jedis的一致性hash代码作为通用工具类

看看人家码神写的代码,这泛型,这继承,这多态用的,写的真是好,代码通用性真是没话说。

在Sharded方法中:

1 ,定义了一个TreeMap ,TreeMap 用于存储虚拟节点(在初始化方法中,将每台服务器节点采用hash算法划分为160个(默认的,DEFAULT_WEIGHT)虚拟节点(当然也可以配置划分权重)

2 ,定义一个LinkedHashMap,用于存储每一个Redis服务器的物理连接,其中shardInfo的createResource就是物理连接信息 。

3,对于key采用与初始化时同样的hash(MurmurHash或者MD5)算法,然后从TreeMap获取大于等于键hash值得节点,取最邻近节点;

4,当key的hash值大于虚拟节点hash值得最大值时(也就是tail为空),取第一个虚拟节点。

相关完整的源码可以查看我的github的intsmaze-hash这个model,传送点https://github.com/intsmaze/intsmaze。

package cn.intsmaze.hash.shard;
public class Sharded<R, S extends ShardInfo<R>> { public static final int DEFAULT_WEIGHT = 1; private TreeMap<Long, S> nodes; private final Hashing algo; private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>(); public Sharded(List<S> shards) {
this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works
// with 64-bits not 128
} public Sharded(List<S> shards, Hashing algo) {
this.algo = algo;
this.shards=shards;
initialize(shards);
} private void initialize(List<S> shards) {
nodes = new TreeMap<Long, S>(); for (int i = 0; i != shards.size(); ++i) {
final S shardInfo = shards.get(i);
if (shardInfo.getTableName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
}
else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash(shardInfo.getTableName() + "*" + shardInfo.getWeight() + n), shardInfo);
}
resources.put(shardInfo, shardInfo.createResource());//调用IntsmazeShardInfo的createResource()方法 如果我们的实现不需要控制远程的连接,那么这个方法就不没什么用
}
} /**
* 这个是找到key对应的节点后,不是仅仅返回属于的节点名称而是返回对应的实例连接
* @param key
* @return
*/
public R getShardByResources(String key) {
return resources.get(getShardInfo(key));
} /**
* 这个是找到key对应的节点后,返回属于的节点名称
* @param key
* @return
*/
public S getShard(String key) {
return getShardInfo(key);
} public S getShardInfo(byte[] key) {
SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
if (tail.isEmpty()) {
return nodes.get(nodes.firstKey());
}
return tail.get(tail.firstKey());
} public S getShardInfo(String key) {
return getShardInfo(SafeEncoder.encode(key));
} } package cn.intsmaze.hash.shard;
public class IntsmazeShardedConnection extends Sharded<Intsmaze, IntsmazeShardInfo>{ public IntsmazeShardedConnection(List<IntsmazeShardInfo> shards) {
super(shards);
} public String getTable(String key) {
IntsmazeShardInfo intsmazeShardInfo = getShard(key);
return intsmazeShardInfo.getTableName();
}
} package cn.intsmaze.hash.shard;
public class IntsmazeShardInfo extends ShardInfo<Intsmaze> { private String host; private int port; public IntsmazeShardInfo(String host, String tableName) {
super(Sharded.DEFAULT_WEIGHT, tableName);
URI uri = URI.create(host);
this.host = uri.getHost();
this.port = uri.getPort();
} @Override
public Intsmaze createResource() {
return new Intsmaze(this);
} } package cn.intsmaze.hash.shard;
public abstract class ShardInfo<T> {
private int weight; private String tableName; public ShardInfo() {
} public ShardInfo(int weight,String tableName) {
this.weight = weight;
this.tableName=tableName;
} protected abstract T createResource(); } package cn.intsmaze.hash.shard;
public class Test { private static IntsmazeShardedConnection sharding; public static void setUpBeforeClass() throws Exception {
List<IntsmazeShardInfo> shards = Arrays.asList(
new IntsmazeShardInfo("localhost:6379", "intsmaze-A"),
new IntsmazeShardInfo("localhost::6379", "intsmaze-B"),
new IntsmazeShardInfo("localhost::6379", "intsmaze-C"),
new IntsmazeShardInfo("localhost::6379", "intsmaze-D"),
new IntsmazeShardInfo("localhost::6379", "intsmaze-E"));
sharding = new IntsmazeShardedConnection(shards);
} public void shardNormal() {
Map<String,Long> map=new HashMap<String,Long>();
for (int i = 0; i < 10000000; i++) { String result = sharding.getTable("sn" + i); Long num=map.get(result);
if(num==null)
{
map.put(result,1L);
}
else
{
num=num+1;
map.put(result,num);
}
}
Set<Map.Entry<String, Long>> entries = map.entrySet();
Iterator<Map.Entry<String, Long>> iterator = entries.iterator();
while(iterator.hasNext())
{
Map.Entry<String, Long> next = iterator.next();
System.out.println(next.getKey()+"--->>>"+next.getValue());
}
} public static void main(String[] args) throws Exception {
Test t=new Test();
t.setUpBeforeClass();
t.shardNormal();
} }

没有热点问题

把jedis的源码提取出来后,跑了一下,发现没有热点问题,原理不是采用算法的问题,而是一个物理节点对应的虚拟节点的数量的问题导致使用hash算法后,还是有热点问题。jedis源码物理节点对应虚拟节点时160,而网上大部分代码都是10以下,所以导致了热点问题,这也告诉我们,实现一致性Hash算法时,不要太吝啬,虚拟节点设置的大点,热点问题就不会再有。

相关完整的源码可以查看我的github的intsmaze-hash这个model,传送点https://github.com/intsmaze/intsmaze。

提取jedis源码的一致性hash代码作为通用工具类的更多相关文章

  1. 读 Kafka 源码写优雅业务代码:配置类

    这个 Kafka 的专题,我会从系统整体架构,设计到代码落地.和大家一起杠源码,学技巧,涨知识.希望大家持续关注一起见证成长! 我相信:技术的道路,十年如一日!十年磨一剑! 往期文章 Kafka 探险 ...

  2. nginx源码分析之hash的实现

    nginx实现了自己的hash数据结构,正如数据结构中讲述的那样,nginx用开放链表法解决冲突,不过不同的是一旦一个hash表被初始化后就不会被修改,即插入和删除,只进行查询操作,所以nginx通过 ...

  3. java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制

    通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...

  4. Jedis源码浅析

    1.概述 Jedis是redis官网推荐的redis java client,代码维护在github https://github.com/xetorthio/jedis. 本质上Jedis帮我们封装 ...

  5. [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构

    [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 目录 [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x0 ...

  6. WeMall微信商城源码活动报名插件代码详情

    WeMall微信商城源码插件活动报名代码是用于商业推广的比较有效的方式,分享了部分比较重要的代码,供技术员学习参考,商家可自由设置报名项目,活动时间,报名内容 代码详情地址:http://addon. ...

  7. WeMall微信商城源码插件大转盘代码详情

    WeMall微信商城源码插件大转盘代码是用于商业推广的比较有效的方式,分享了部分比较重要的代码,供技术员学习参考 代码详情地址:http://addon.wemallshop.com/Product/ ...

  8. 手机自动化测试:Appium源码分析之跟踪代码分析九

    手机自动化测试:Appium源码分析之跟踪代码分析九   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  9. 手机自动化测试:Appium源码分析之跟踪代码分析八

    手机自动化测试:Appium源码分析之跟踪代码分析八   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

随机推荐

  1. java笔记----JVM内存

    运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器 虚拟机栈区 :也就是我们常说的栈区,线程私有,存放基本类型,对象的引用和 returnAddress ,在编译期间完成分配. 堆区  ...

  2. mssql sql server 其它系统函数 parsename 点语法字符串分割函数应用简介

    转自:http://www.maomao365.com/?p=4534 一. parsename函数功能简介 parsename函数的主要功能是:可以快速的使用”.”关键字分解字符串,并返回.分解后指 ...

  3. 探索SQL Server元数据(二)

    背景 上一篇中,我介绍了SQL Server 允许访问数据库的元数据,为什么有元数据,如何使用元数据.这一篇中我会介绍如何进一步找到各种有价值的信息.以触发器为例,因为它们往往一起很多问题. 那么如何 ...

  4. c/c++ 用前序和中序,或者中序和后序,创建二叉树

    c/c++ 用前序和中序,或者中序和后序,创建二叉树 用前序和中序创建二叉树 //用没有结束标记的char*, clr为前序,lcr为中序来创建树 //前序的第一个字符一定是root节点,然后去中序字 ...

  5. vmware中连接U盘

    1.首先安装VMware tools. 2.然后根据这篇经验操作  https://jingyan.baidu.com/article/7c6fb42828806480652c9062.html 3. ...

  6. windows平台下实现高可用性和可扩展性-ARR和HLB

    本文档提供了关于如何将应用程序请求路由(ARR)与硬件负载均衡器一起使用以实现高可用性和可伸缩性的说明性指导.本文采用F5大IP负载均衡器来说明ARR与硬件负载平衡器之间的工作关系. IIS7.0及以 ...

  7. February 15th, 2018 Week 7th Thursday

    Every orientation presupposes a disorientation. 迷失过方向,才能找到方向. Not until we are lost do we begin to u ...

  8. nuxt拦截IE浏览器

    需求场景 判断浏览器类型,让譬如IE的低版本浏览器跳转到指定提示浏览器升级页面. 难点分析 使用过的都知道,nuxt没有暴露主入口页面也就是index.html啊,我们以前常用的IE条件判断没地方写. ...

  9. Zookeeper的一致性

    转载:http://flyfoxs.iteye.com/blog/2121560 下面内容主要摘抄于<<Hadoop实战>>,红色高亮部分是本人添加的白话注释. Zookeep ...

  10. 并发的HashMap为什么会引起死循环?

    转载:http://blog.csdn.net/zhuqiuhui/article/details/51849692 今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMa ...