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

一致性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。

原文地址:https://www.cnblogs.com/intsmaze/p/9629967.html

时间: 2024-10-12 09:22:50

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

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

通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的引用放入数组中,每一个数组元素都是一个引用变量. 实际上,HashSet 和 HashMap 之间有非常多相似之处,对于 HashSet 而言.系统採用 Hash 算法决定集合元素的存储位置,这样能够保证能高速存.取集合元素:对于 HashMap 而言.系统 key-value 当成一个总体进行处理

stl源码分析之hash table

本文主要分析g++ stl中哈希表的实现方法.stl中,除了以红黑树为底层存储结构的map和set,还有用哈希表实现的hash_map和hash_set.map和set的查询时间是对数级的,而hash_map和hash_set更快,可以达到常数级,不过哈希表需要更多内存空间,属于以空间换时间的用法,而且选择一个好的哈希函数也不那么容易. 一. 哈希表基本概念 哈希表,又名散列表,是根据关键字直接访问内存的数据结构.通过哈希函数,将键值映射转换成数组中的位置,就可以在O(1)的时间内访问到数据.举

&lt;&lt;&lt; Java提取网页源码

package com.sevennight; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; public class zidongwenzi { /** * @param args * @param * @throws IOException

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

WeMall微信商城源码插件活动报名代码是用于商业推广的比较有效的方式,分享了部分比较重要的代码,供技术员学习参考,商家可自由设置报名项目,活动时间,报名内容 代码详情地址:http://addon.wemallshop.com/Product/addonList/menu_id/1 或 www.wemallshop.com AdminController.class <?php namespace Addons\Apply\Controller; class AdminController e

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

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

在Android源码中查找Java代码中native函数对应的C++实现

Android源码中很多关键代码都是C++实现的,java通过jni来调用,经常会看到java中这样的代码: static native Thread currentThread(); 如何根据方法名找到其对应的C++实现,有两个方法. 先来个java代码的示例VMThread.java: package java.lang; class VMThread { Thread thread; int vmData; VMThread(Thread t) { thread = t; } native

Cordova Android源码分析系列二(CordovaWebView相关类分析)

本篇文章是Cordova Android源码分析系列文章的第二篇,主要分析CordovaWebView和CordovaWebViewClient类,通过分析代码可以知道Web网页加载的过程,错误出来,多线程处理等. CordovaWebView类分析 CordovaWebView类继承了Android WebView类,这是一个很自然的实现,共1000多行代码.包含了PluginManager pluginManager,BroadcastReceiver receiver,CordovaInt

Spring源码分析——BeanFactory体系之抽象类、类分析(二)

上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之抽象类.类分析(一),今天继续分析. 一.工厂Bean注册支持——FactoryBeanRegistrySupport 废话不多说,直接看我注释的源码: /* * Copyright 2002-2012 the original author or authors. * * Licensed und

Python源码 -- C语言实现面向对象编程(基类&amp;派生类&amp;多态)

由于公司的ERP系统数据量大,有40G+的数据,并发用户90+,连接数1000+,原来的IO性能跟不上用户的操作响应需求,报表查询慢,该做的索引都做过了,索引每周重建一次,还是解决不了问题,为此,公司新购了dell poweredge R720xd服务器,该套服务器支持2个阵列,24个硬盘,双CPU,64G内存,比较牛X的了,我们创建两个raid,两块SAS硬盘做Raid1,6块SSD硬盘做Raid10,系统装好了,高兴呀,但结过测试发现,总有一个raid速度相当慢,和台式机速度一样,我地妈呀,