Redis实现分布式存储

Memcache是在服务端实现分片的分布式的缓存系统,而Redis是基于Master-Slave(主从),如果想把Reids做成分布式缓存,就要多做几套Master-Slave,每套Master-Slave完成各自的容灾处理,另外,Redis只能在客户端完成分片。

Redis有中语言的客户端,其中基于Java语言的客户端叫做Jedis,Jedis客户端已经为Redis实现了分布式存储。下面分别介绍了Jedis分布式存储的简单使用以及Spring与Jedis的整合使用。

一、Jedis分布式存储举例

package com.ghs.test;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

public class TestShardJedis {
    ShardedJedisPool pool = null;
    ShardedJedis jedis = null;

    @Before
    public void setup(){
        JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.1.108");
        JedisShardInfo shardInfo2 = new JedisShardInfo("...");
        List<JedisShardInfo> shardInfos = new ArrayList<JedisShardInfo>();
        shardInfos.add(shardInfo1);
        shardInfos.add(shardInfo2);
        pool = new ShardedJedisPool(new GenericObjectPoolConfig(), shardInfos);
    }

    @Test
    public void testShard(){
        jedis = pool.getResource();
        //CRUD
        jedis.set("name", "zhangsan");
        System.out.println(jedis.get("name"));
        jedis.del("name");

        //释放对象
        pool.returnResource(jedis);
    }
}

二、Spring+Jedis实现分布式存储

spring中的配置:
这里只是把示例一中JedisShardInfo和ShardJedisPool的示例交给Sping容器管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <context:property-placeholder location="classpath:redis.properties" />
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxActive" value="${redis.pool.maxActive}" />
        <property name="maxIdle" value="${redis.pool.maxIdle}" />
        <property name="maxWait" value="${redis.pool.maxWait}" />
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
    </bean>
    <bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
        <constructor-arg index="0" ref="jedisPoolConfig" />
        <constructor-arg index="1">
            <list>
                <bean class="redis.clients.jedis.JedisShardInfo">
                    <constructor-arg index="0" value="${redis1.ip}" />
                    <constructor-arg index="1" value="${redis.port}"
                        type="int" />
                </bean>
                <bean class="redis.clients.jedis.JedisShardInfo">
                    <constructor-arg index="0" value="${redis2.ip}" />
                    <constructor-arg index="1" value="${redis.port}" type="int" />
                </bean>
            </list>
        </constructor-arg>
    </bean>
</beans>

对访问操作进行封装:

package com.ghs.test;

import java.util.List;

import javax.annotation.Resource;

import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

public class SpringRedisClient implements IRedisClient{

    @Resource(name="shardedJedisPool")
    private ShardedJedisPool shardedJedisPool;

    private ShardedJedis getResource(){
        return shardedJedisPool.getResource();
    }

    @Override
    public String set(String key,String value){
        return getResource().set(key,value);
    }
    @Override
    public String get(String key){
        return getResource().get(key);
    }

    @Override
    public Long del(String key) {
        return getResource().del(key);
    }

    @Override
    public Long lpush(String key, String... strings) {
        return getResource().lpush(key, strings);
    }

    @Override
    public Long rpush(String key, String... strings) {
        return getResource().rpush(key, strings);
    }

    @Override
    public List<String> lrange(String key, int start, int end){
        return getResource().lrange(key, start,end);
    }

    //…………………………
}

三、Jedis分布式存储实现原理

1、JedisShardInfo类
这个类封装了Redis主机的一些基本信息:

  private int timeout;
  private String host;
  private int port;
  private String password = null;
  private String name = null;

最重要的是它的父类中有一个weight字段,作为本Redis服务器的权值。
这个类还有一个继承自父类的方法createResource(),用来生成这个Redis服务器对应的Jedis对象,即往Redis服务器存取数据的对象。

2、Sharded类
我们先来熟悉一下一致性哈希算法:
http://blog.csdn.net/u011983531/article/details/49507729

对一致性哈希算法熟悉以后,对Sharded类的理解就不难了,通过这个类来为每个分片创建虚拟节点,为每次操作获取分片。
Sharded中的三个字段,nodes是用来模拟一致性哈希算法用的;algo是用来对字符串产生哈希值的hash函数,这里默认的是murmurhash,这个算法的随机分布特征表现比较好;resources这个map是用来存储JedisShardInfo与其对应的Jedis类之间的映射关系。

  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>();//每个机器节点关联的虚拟节点

下面我们来看看初始化操作和获取分片的操作:

  //初始化操作,为每个主机管理虚拟节点
  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 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());
    }
  }

在for循环中,遍历主机列表(shards.get(i)),之后对每个主机按照单权重160的比例计算shard值,将shard值和主机信息(shardInfo)放到nodes中,将主机信息(shardInfo)和其对应的链接资源(Jedis)映射放入到resources中。

Weight是权重,用于调节单个主机被映射值个数,如果weight为1,那么当前主机将被映射为160个值,weight为2,当前主机将被映射为320个值,因此weight为2的节点被访问到的概率就会高一些。

遍历list中的每一个shardInfo,将其权重weight*160生成n,然后用名字或者编号来生成n个哈希值(这个是为了保证哈希算法的平衡性而生成的虚拟节点),然后将其和本shardInfo的对应关系存储到treemap里面(这是在模拟一致性哈希算法中将虚拟节点映射到环上的操作),最后将shardInfo与对应的Jedis类的映射关系存储到resources里面。

   //获取分片
    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(getKeyTag(key)));
  }

首先根据传入的key按照hash算法(默认为murmurhash)取得其value,然后用这个value到treemap中找key大于前面生成的value值的第一个键值对,这个键值对的value既是对应的shardedInfo。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值