如何简单地实现易用的ShardedJedisSentinelPool
来源:互联网 发布:带宽测试软件 编辑:程序博客网 时间:2024/04/29 22:56
Jedis包中有个很恶心的问题,那就是包里面有支持分片的ShardeJedis客户端类,也有支持哨兵的池类JedisSentinelPool,就是没有既支持分片又支持哨兵的池类,所以必须自己自定义一个ShardedJedisSentinelPool,定义这个类,在网上有个很受欢迎的版本,是继承了Pool<ShardeJedis>类后重写相关的池操作的方法,个人觉得这种方案太麻烦,而且据反馈也有很多考虑不全面的细节,造成bug。一开始我试图去继承JedisSentinelPool类并重写相关方法把分配功能加进去,发现很难搞。后来改变了思路,其实可以建立一个JedisSentinelPool数组,每个元素其实对应监视一个主备的redis服务节点,然后可以根据分片规则去确定从哪个JedisSentinelPool中获取jedis。这个方案比较简单实用,唯一难点就是要去实现跟ShardeJedis一样的一致性哈希分片规则。好在sharde.java中有相关源码,只要稍作修改即可。实现源码如下:
public class ShardedJedisSentinelPool {private Map<String,JedisSentinelPool> poolMap = new HashMap<String,JedisSentinelPool>();
private Hashing algo = Hashing.MURMUR_HASH;
private TreeMap<Long, JedisShardInfo> nodes;
public ShardedJedisSentinelPool(Set<HostAndPort> hostInfo, Set<String> sentinels){
List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
for(HostAndPort iter:hostInfo){
JedisShardInfo info = new JedisShardInfo(iter.getHost(),iter.getPort());
list.add(info);
}
initialize(list);
for(HostAndPort iter:hostInfo){
String masterName = "master-"+iter.getHost()+":"+iter.getPort();
JedisSentinelPool sentinuelPool = new JedisSentinelPool(masterName, sentinels);
poolMap.put(masterName, sentinuelPool);
}
}
public Jedis getResource(String key){
JedisShardInfo shardInfo = getShardInfo(key.getBytes());
String masterName = "master-"+shardInfo.getHost()+":"+shardInfo.getPort();
JedisSentinelPool sentinuelPool = poolMap.get(masterName);
Jedis sentinueJedis = sentinuelPool.getResource();
return sentinueJedis;
}
private void initialize(List<JedisShardInfo> shards) {
nodes = new TreeMap<Long, JedisShardInfo>();
for (int i = 0; i != shards.size(); ++i) {
final JedisShardInfo 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);
}
}
}
public JedisShardInfo getShardInfo(byte[] key) {
SortedMap<Long, JedisShardInfo> tail = nodes.tailMap(algo.hash(key));
if (tail.isEmpty()) {
return nodes.get(nodes.firstKey());
}
return tail.get(tail.firstKey());
}
public void close(){
for(Entry<String, JedisSentinelPool> iter:poolMap.entrySet()){
iter.getValue().destroy();
}
}
}
--在这个方法中,构造函数先初始化了分片规则得到了每个主节点和160个虚拟节点的映射关系TreeMap<Long, JedisShardInfo> nodes,然后初始化了JedisSentinelPool数组,对于每个需要根据key获取jedis的用户,先经过hash(key),然后nodes.get(hash(key))得到对应的JedisShardInfo,然后根据JedisShardInfo对象获取服务主节点的ip和端口号组成masterName,这样就可以根据masterName去哨兵池JedisSentinelPool动态获取真正可用的jedis对象。这样一来如果某个redis服务主节点挂了,哨兵集群会发现并且把从节点当作新的主节点。这样就能达到高可用的效果。
其中的一致性哈希算法过程稍微解释下:
当我们传入一个JedisShardInfo数组进去后,就会为每个JedisShardInfo对象分配160个虚拟节点,每个虚拟节点的可key由this.algo.hash("SHARD-" + i +"-NODE-" + n)或者this.algo.hash(shardInfo.getName() +"*" + shardInfo.getWeight() + n)计算得出,这样就形成160个分散的虚拟key和1个JedisShardInfo的映射关系的map, JedisShardInfo数组里每个JedisShardInfo都会得到160个这样的映射关系(虚拟节点),并且最后都全部加入大的map(TreeMap<Long, JedisShardInfo> nodes)中。当我们需要获取某个真实key值对应的真正JedisShardInfo(里面包含服务节点的ip和port信息),就对该key也执行hash操作algo.hash(key),然后通过TreeMap.tailMap获取虚拟节点的key大于或等于algo.hash(key)的第一个虚拟节点已经该虚拟节点对应的JedisShardInfo,如果algo.hash(key)大于所有虚拟节点的key ,那么由于一致性哈希环的概念,该key将取所有虚拟节点中的第一个(也就是最小那一个)并获取第一个虚拟节点对应的JedisShardInfo。
这里还有个小的知识点就是TreeMap或则TreeXXXX的容器的作用一般就是通过红黑树的数据结构进行了排序,它们的作用就是这是一个有序的容器。- 如何简单地实现易用的ShardedJedisSentinelPool
- 如何简单地实现引用类型的深度克隆
- 如何实现一个简单地Filter
- 简单地实现图像坐标的信息
- 一个简单地Shell-like 的实现
- 如何简单地找回保存在浏览器里的密码
- Jquery简单地实现ajax
- 简单地实现在文本框中的输入是大写的。
- 简单的 Android 调用WebService 实现号码归属地查询
- java简单地实现Tiny语言的词法分析器
- 用gSOAP更简单地实现Web Services Client
- 简单地用nfs实现linux间文件共享
- 用gSOAP更简单地实现Web Services Client
- 用JAVA实现一个简单地Http服务器
- 如何简单地测算系统吞吐量
- 如何简单地理解快速排序
- CoreData简单地的使用
- zTree 简单地实现异步加载
- 关于HTML基础的总结
- Oracle练习2-习题及答案
- redis面试总结
- 【转】Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)
- TLV解析java
- 如何简单地实现易用的ShardedJedisSentinelPool
- 如何快速接手并熟悉新项目,刚刚经历.
- [LeetCode]112. Path Sum
- Java 敏感词过滤
- java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\xB3' for column 'Content' at row 1
- 公司实习对前端ajax,jquery的认识
- 出现java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener解决方案
- hihoCoder1040:矩形判断
- arm开发板使用ntp与服务器同步时间