Jedis路由key的算法剝離

在Redis集羣中,會有不少個分片,若是此時利用Jedis來操做此Redis集羣,那麼他會把數據路由到不到的分片上。並且若是動態的往集羣中增長分片,也不會影響Jedis的功能。到底是怎麼作到的呢?css

因爲最近公司要集中遷移redis集羣,也就是把舊集羣的數據遷移到Redis Cluster中,就須要咱們本身來整理數據。恰好我這裏有個庫存熱點數據,咱們叫作A吧,這個A在Redis集羣中,每一個分片上都有數據。好比,Redis集羣有4個片,而我總庫存量爲1000,那麼會在4個片上放上Key A,每一個A中庫存量爲250.html

新的Redis cluster,爲了防止熱點庫存問題,也就申請了4個cluster,每一個cluster至關於以前的一個分片。數據在寫入的時候,每一個cluster會寫一個key A,庫存量爲250.node

如今問題來了,有一些Key,好比說B,在原來的Redis集羣中,是利用Jedis路由來導向某一片寫入的,假設這裏寫入的是第3片。若是遷移到新的4個Redis cluster中,勢必須要寫到第三個Redis Cluster中。由於數據遷移,確定是第一片redis數據遷移到第一個Cluster,第二片redis數據遷移到第二個Cluster,以此類推。redis

因此這裏須要咱們來實現和Jedis同樣的路由算法,按照Jedis提供的類,咱們實現以下:算法

首先,定義好ShardNode,繼承自Jedis的ShardInfo:app

public class ShardNode implements ShardInfo {

    public ShardNode(String index, Cluster cluster) {
        this.index = index;
        this.cluster = cluster;
    }

    private String index;

    private Cluster cluster;

    @Override
    public int getWeight() {
        return 1;
    }

    @Override
    public String getName() {
        return null;
    }

    public Cluster getCluster() {
        return cluster;
    }

    public void setCluster(Cluster cluster) {
        this.cluster = cluster;
    }

    public String getIndex() {
        return index;
    }

    public void setIndex(String index) {
        this.index = index;
    }
}

注意,getName方法裏面必定要return null, 這樣就會根據配置的分片的數量前後順序來運算哈希key。具體源碼以下(翻看KetamaHashing類的源碼):dom

 

    protected void initialize(List<T> shards) {
        Charset charset = Charset.forName("utf-8");
        String key = null;

        for(int i = 0; i < shards.size(); ++i) {
            T shardInfo = (ShardInfo)shards.get(i);
            if (shardInfo == null) {
                throw new IllegalArgumentException("shard element #" + i + " is null.");
            }

            AtomicReference<T> wrapper = new AtomicReference(shardInfo);
            this.originals.add(wrapper);

            for(int n = 0; n < 160 * shardInfo.getWeight(); ++n) {
                if (shardInfo.getName() == null) {
                    key = "SHARD-" + i + "-NODE-" + n;
                } else {
                    key = shardInfo.getName() + "*" + shardInfo.getWeight() + n;
                }

                this.nodes.put(this.algo.hash(key.getBytes(charset)), wrapper);
            }
        }

    }
 

注意我標黃顏色部分,正式由於shardInfo.getName爲null,因此咱們的路由算法纔可以按照配置的分片順序進行路由。 ide

而後進行實現便可:測試

 public Cluster getCluster(String key) {
        KetamaHashing  ketamaHashing = new KetamaHashing(shardNodes, new MurmurHash());
        ShardNode shard = (ShardNode) ketamaHashing.getShardInfo(key.getBytes());
        return shard.getCluster();
    }

這樣咱們就能夠經過key來獲取新的Redis Cluster實例了。經過測試用例結果,咱們也能夠看出Jedis路由算法和咱們所寫的路由算法是一致的。this

 @Test
    public void testJedisHash() {

        Map<String, String> map = new HashMap<>();
        map.put("192.168.155.84:6379", "0");
        map.put("192.168.155.84:6390", "1");
        map.put("192.168.155.85:6379", "2");
        map.put("192.168.155.85:6390", "3");

        String key;
        List<String> list = new ArrayList<>();


        for (int i = 0; i < 1000; i++) {

            key = UUID.randomUUID().toString();

            ShardNode cluster = storeJimdbs.getShardNode(key);

            ShardInfo redisShard = jrc.getShardInfo(key);
            String shardKey = redisShard.toString().split("/")[0];

            if (cluster.getIndex().equals(map.get(shardKey))) {
                list.add("OK");
            } else {
                System.out.println("NNNNNNNotMatch!!!!!!!");
            }
        }

        System.out.println(list.size());
    }

最後運算出來的結果是1000,也就是說1000個隨機key,利用Jedis路由算法操做,利用咱們寫的路由算法操做,實際上是打到相同的分片序號上的。

也就是0號分片對應0號cluster。

相關文章
相關標籤/搜索