jedis源码分析(三)-多节点实例

我们现实场景中经常会遇到多个redis服务节点的情况, jedis提供实现分片存储的实现,jedis通过ShardedJedis支持多个节点地址,简单的shardedJedis代码示例:
3,多实例模式:
import java.util.ArrayList;
import java.util.List;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
public class ShardedJedisUtil {
       private ShardedJedis shardedJedis;
       private String addresss;
       public ShardedJedisUtil(String addresss) {
              this.addresss = addresss;
              initShardedJedis();
       }
       public void initShardedJedis() {
              List<JedisShardInfo> shards = new ArrayList<>();
              JedisShardInfo info = null;
              for (String address : addresss.split(",")) {
                     String[] hostAndPort = address.split(":");
                     info = new JedisShardInfo(hostAndPort[0], Integer.valueOf(hostAndPort[1]));
                     shards.add(info);
              }
              shardedJedis = new ShardedJedis(shards);
       }
       public void set(String key, String value) {
              try {
                     shardedJedis.set(key, value);
              } catch (JedisConnectionException e) {
                     System.out.println(e.getMessage());
              } finally {
                     shardedJedis.close();
              }
       }
       public String get(String key) {
              String result = null;
              try {
                     result = shardedJedis.get(key);
              } catch (JedisConnectionException e) {
                     System.out.println(e.getMessage());
              } finally {
                     shardedJedis.close();
              }
              return result;
       }
       public static void main(String args[]) {
              for (int i = 0; i < 100; i++) {
                     ShardedJedisUtil ShardedJedisUtil = new ShardedJedisUtil("127.0.0.1:4100,127.0.0.1:4101");
                     ShardedJedisUtil.set("key" + i, "value" + i);
                     System.out.println(ShardedJedisUtil.get("key" + i));
              }
       }
}

在示例构造方法中调用initShardedJedis()方法,首先创建一个JedisShardInfo对象的List集合shards,将传入的地址解析之后创建两个JedisShardInfo对象放入shards中,JedisShardInfo是继承了ShardInfo<Jedis>类的一个包含redis节点信息的包装类对象
public class JedisShardInfo extends ShardInfo<Jedis> {
 
  private int connectionTimeout;
  private int soTimeout;
  private String host;
  private int port;
  private String password = null;
  private String name = null;
  private int db = 0;
  private boolean ssl;
  private SSLSocketFactory sslSocketFactory;
  private SSLParameters sslParameters;
  private HostnameVerifier hostnameVerifier;
 
  public JedisShardInfo(String host) {
    super(Sharded.DEFAULT_WEIGHT);
    URI uri = URI.create(host);
    if (JedisURIHelper.isValid(uri)) {
      this.host = uri.getHost();
      this.port = uri.getPort();
      this.password = JedisURIHelper.getPassword(uri);
      this.db = JedisURIHelper.getDBIndex(uri);
      this.ssl = uri.getScheme().equals("rediss");
    } else {
      this.host = host;
      this.port = Protocol.DEFAULT_PORT;
    }
  }
  public JedisShardInfo(String host, SSLSocketFactory sslSocketFactory,
      SSLParameters sslParameters, HostnameVerifier hostnameVerifier) {
    super(Sharded.DEFAULT_WEIGHT);
    URI uri = URI.create(host);
    if (JedisURIHelper.isValid(uri)) {
      this.host = uri.getHost();
      this.port = uri.getPort();
      this.password = JedisURIHelper.getPassword(uri);
      this.db = JedisURIHelper.getDBIndex(uri);
      this.ssl = uri.getScheme().equals("rediss");
      this.sslSocketFactory = sslSocketFactory;
      this.sslParameters = sslParameters;
      this.hostnameVerifier = hostnameVerifier;
    } else {
      this.host = host;
      this.port = Protocol.DEFAULT_PORT;
    }
  }
  public JedisShardInfo(String host, String name) {
    this(host, Protocol.DEFAULT_PORT, name);
  }
  public JedisShardInfo(String host, int port) {
    this(host, port, 2000);
  }
.....
}
之后创建了ShardedJedis对象 ,在ShardedJedis构造方法中
 public ShardedJedis(List<JedisShardInfo> shards) {
    super(shards);
  }
  public ShardedJedis(List<JedisShardInfo> shards, Hashing algo) {
    super(shards, algo);
  }
  public ShardedJedis(List<JedisShardInfo> shards, Pattern keyTagPattern) {
    super(shards, keyTagPattern);
  }
  public ShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
    super(shards, algo, keyTagPattern);
  }
构造参数有一个JedisShardInfo对象的集合,JedisShardInfo对象中包含host,port,password等属性信息,
ShardedJedis继承了BinaryShardedJedis类,BinaryShardedJedis继承了Sharded<Jedis, JedisShardInfo>,并实现了BinaryJedisCommands接口,Sharded的构造方法
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;
              initialize(shards);
       }
       public Sharded(List<S> shards, Pattern tagPattern) {
              this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good
              // as we works with
              // 64-bits not 128
       }
       public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
              this.algo = algo;
              this.tagPattern = tagPattern;
              initialize(shards);
       }
Sharded构造方法中有一个参数Hashing.MURMUR_HASH,Austin Appleby在2008年发明,之后衍生出MURMUR2,MURMUR3等版本,Hashing.MURMUR_HASH是一个高运算低碰撞的哈希算法,
Sharded构造方法中还调用了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.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());
              }
       }
initialize方法实现了对redis节点的分片处理,存放在定义TreeMap类型的nodes中,nodes的key通过murmur_hash哈希算法计算的哈希值,value值是之前传入的JedisShardInfo对象,默认分片大小是160*weight,weight是权重大小,默认为1。最后将JedisShardInfo创建的资源放入resuources的LinkedHashMap中。
初始化shardedJedis对象完成之后,调用set方法,
 public String set(String key, String value) {
    Jedis j = getShard(key);
    System.out.println("master:"+j.getClient().getHost()+":"+j.getClient().getPort());
    return j.set(key, value);
  }
在set方法中首先调用了一个getShard(key)方法,该方法获取分片后的具体某一个分片信息,也就是分片中放入的jedis对象,
public R getShard(String key) {
              return resources.get(getShardInfo(key));
       }
public S getShardInfo(String key) {
              return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
       }
public S getShardInfo(byte[] key) {
              SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
              S s = null;
              Long num = null;
              if (tail.isEmpty()) {
                     num = nodes.firstKey();
                     s = nodes.get(num);
                     return s;
              }
              num = tail.firstKey();
              s = tail.get(num);
              return s;
       }
最终调用到getShardInfo(byte[] key)方法,该方法中nodes调用tailMap方法,查询文档可知tailMap方法用于返回此映射,其键大于或等于fromKey的部分视图。nodes.tailMap(algo.hash(key))将nodes集合中将key键的值大于等于algo.hash(key)哈希值的所有键值对都返回。这里采用的哈希算法与初始化shardedJedis的分片算法一致都采用Murmur_hash算法。
然后通过tail的firstKey方法获取到第一个键值对的键值,也就是一个第一个键值对的key,在通过tail.get(key)获取到初始化时放入的Jedis对象。最后调用jedis的set方法将key,value放入其中。
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页