Sharded實現學習-我們到底能走多遠系列(32)


我們到底能走多遠系列(32)

扯淡:

  工作是容易的賺錢是困難的
  戀愛是容易的成家是困難的
  相愛是容易的相處是困難的
  決定是容易的可是等待是困難的

 

主題:

1,Sharded的實現

 
 ShardedJedis是基於一致性哈希算法實現的分布式Redis集群客戶端。
 
 關於一致性哈希算法 可以參考 轉載文章
 

  Memcached 和 redis 都使用了該算法來實現自己的多服務器均勻分派存儲值的。

 

  shardedJedisPool的配置如下:(具體可以參考《spring和redis的整合》

 

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool"  scope="singleton">
        <constructor-arg index="0" ref="jedisPoolConfig" />
        <constructor-arg index="1">
            <list>
                <bean class="redis.clients.jedis.JedisShardInfo">
                    <constructor-arg name="host" value="${redis.host}" />
                    <constructor-arg name="port" value="${redis.port}" />
                    <constructor-arg name="timeout" value="${redis.timeout}" />
                    <constructor-arg name="weight" value="1" />
                </bean>
            </list>
        </constructor-arg>
 </bean>

 

 注入了兩個對象:jedisPoolConfig 和 JedisShardInfo

然后產生ShardedJedis:

    public ShardedJedis getRedisClient() {
        try {
            ShardedJedis shardJedis = shardedJedisPool.getResource();
            return shardJedis;
        } catch (Exception e) {
            log.error("getRedisClent error", e);
        }
        return null;
    }

ShardedJedis 繼承 BinaryShardedJedis 繼承 Sharded<Jedis, JedisShardInfo>

 

Sharded的實現就是前面一致性哈希算法的實現啦~

// 使用TreeMap來完成構造出一個很多節點的環形
private TreeMap<Long, S> nodes;

// 構造方法
public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
        this.algo = algo;
        this.tagPattern = tagPattern;
        // 初始化方法,建立一個個節點
        initialize(shards);
}

initialize方法:

 

    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.getName() == null)
                for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
                    nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
                }
            else
                // 將設置的權重放大160倍,產生更多的節點,因為hash一下就散落到各道各處了,如此就是所謂的虛擬節點,以保證均勻分布
                for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
                    nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);
                }
            resources.put(shardInfo, shardInfo.createResource());
        }
    }

 

redis放key value的時候,需要判斷應該放在那個服務器上,就是判斷hash后更靠近哪個節點。

    public R getShard(byte[] key) {
        return resources.get(getShardInfo(key));
    }

    public R getShard(String key) {
        return resources.get(getShardInfo(key));
    }
    //最終調用方法
    public S getShardInfo(byte[] key) {
       // 首先判斷是不是tree中最大的key,及最后一個,注意我們是環,所以最大的后面就要從頭開始。
        SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
        // 是最后一個key了,所以取第一個節點對應的服務器
        if (tail.size() == 0) {
            return nodes.get(nodes.firstKey());
        }
        // 不是最后一個就是比自己離自己最近的大的key對應的服務器
        return tail.get(tail.firstKey());
    }

    public S getShardInfo(String key) {
        return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
    }

 

到這里基本明白了如何抽象實現一個環狀的排序的數據結構了。值得借鑒。

 

2,實踐中的一個例子

 問題:模擬一個抽獎的效果,隨機產生一個范圍內的數字,看是否在中獎的區域內來判斷是否中獎。 中獎區域分多個層次的獎項。
如圖:
 
|0 -------獎項1--------200|201-------獎項2--------1000|1001-------獎項3-------5000|5001-------沒獎---------100000|
 
使用了TreeMap來實現
從項目里拉出來的代碼:
TreeMap<Integer, AwardConfigDO> extentTree = new TreeMap<Integer, AwardConfigDO>();
        // 獲獎區間划分
        for (AwardConfigDO awardConfig : configList) {
            //Probability是區間節點,如100,500
            extentTree.put(awardConfig.getProbability(), awardConfig);
        }
        // 進入中獎區 random 是隨機產生的數字,首先判斷是否進入中獎區
        if (random < extentTree.lastKey()) {
            //然后判斷 中獎獎項 是哪個
            AwardConfigDO awardConfig =  extentTree.higherEntry(random).getValue();
        }    

 

所以TreeMap可以來抽象實現這種區間的結構。關於TreeMap可以看API哦。

 

--------------------20130827補充-----------------------

需要注意的是 使用了TreeMap 需要考慮key相同的情況,這種情況就需要接受前一個映射關系會被替換的情況。

public static void main(String[] args) {
        TreeMap<Integer, String> extentTree = new TreeMap<Integer, String>();
        extentTree.put(1, "1");
        extentTree.put(10, "2");
        extentTree.put(10, "3");
        extentTree.put(100, "4");
        String value = extentTree.higherEntry(5).getValue();
        System.out.println(value);//output:3
    }

 

 

 

讓我們繼續前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不會成功。
共勉。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM