Jedis Cluster源码分析
最近一个项目用到Jedis客户端,需要对这个客户端进行改造。看了一下Jedis Cluster源码,做个记录
首先,说核心内容, 在Jedis源码中,关于cluster有个两个重要的map。一个是nodes,一个是slots
nodes: host:port ----> JedisPool
slots: slot ----> JedisPool
nodes存放的是key为host:port到JedisPool的映射
slots存放的 slot到JedisPool的映射
这里,JedisPool是用apache common pool存放jedis对象的pool,slot是通过Crc16算出对16384取余得到
上个Jedis Cluster的Demo吧
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set; /**
* Created by guanxianseng on 2017/8/15.
*
* nodes: host:port -> JedisPool
* slots: slot -> JedisPool
*/
public class TestCluster {
public static void main(String[] args) {
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("192.168.211.131", 7340));
jedisClusterNodes.add(new HostAndPort("192.168.211.131", 7341));
jedisClusterNodes.add(new HostAndPort("192.168.211.131", 7342));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("name", "guanxianseng");
System.out.println(jc.get("name"));
}
}
输出
guanxianseng Process finished with exit code 0
这里IP是我的虚拟机的IP,开了两台虚拟机,部署的是三主三从的集群
首先,进入JedisCluster的构造函数,一路找下去,我们会看到这样的代码
public JedisClusterConnectionHandler(Set<HostAndPort> nodes,
final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password) {
this.cache = new JedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password);
initializeSlotsCache(nodes, poolConfig, password);
}
进入initializeSlotsCache方法
private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig, String password) {
for (HostAndPort hostAndPort : startNodes) {
Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort());
if (password != null) {
jedis.auth(password);
}
try {
cache.discoverClusterNodesAndSlots(jedis);
break;
} catch (JedisConnectionException e) {
// try next nodes
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
这里,获取集群节点的jedis对象,进入discoverClusterNodesAndSlots(jedis)
public void discoverClusterNodesAndSlots(Jedis jedis) {
w.lock();
try {
reset();
List<Object> slots = jedis.clusterSlots();
for (Object slotInfoObj : slots) {
List<Object> slotInfo = (List<Object>) slotInfoObj;
if (slotInfo.size() <= MASTER_NODE_INDEX) {
continue;
}
List<Integer> slotNums = getAssignedSlotArray(slotInfo);
// hostInfos
int size = slotInfo.size();
for (int i = MASTER_NODE_INDEX; i < size; i++) {
List<Object> hostInfos = (List<Object>) slotInfo.get(i);
if (hostInfos.size() <= 0) {
continue;
}
HostAndPort targetNode = generateHostAndPort(hostInfos);
setupNodeIfNotExist(targetNode);
if (i == MASTER_NODE_INDEX) {
assignSlotsToNode(slotNums, targetNode);
}
}
}
} finally {
w.unlock();
}
}
第6行,其实就是执行slots命令。进入getAssignedSlotArray方法
private List<Integer> getAssignedSlotArray(List<Object> slotInfo) {
List<Integer> slotNums = new ArrayList<Integer>();
for (int slot = ((Long) slotInfo.get(0)).intValue(); slot <= ((Long) slotInfo.get(1))
.intValue(); slot++) {
slotNums.add(slot);
}
return slotNums;
}
这里获取了,节点分配的slots
回到上面,进入generateHostAndPort方法
private HostAndPort generateHostAndPort(List<Object> hostInfos) {
return new HostAndPort(SafeEncoder.encode((byte[]) hostInfos.get(0)),
((Long) hostInfos.get(1)).intValue());
}
这里获取到节点的host和port
回到上面,进入setupNodeIfNotExist(targetNode);
public JedisPool setupNodeIfNotExist(HostAndPort node) {
w.lock();
try {
String nodeKey = getNodeKey(node);
JedisPool existingPool = nodes.get(nodeKey);
if (existingPool != null) return existingPool;
JedisPool nodePool = new JedisPool(poolConfig, node.getHost(), node.getPort(),
connectionTimeout, soTimeout, password, 0, null, false, null, null, null);
nodes.put(nodeKey, nodePool);
return nodePool;
} finally {
w.unlock();
}
}
这里设置我们一开始提到的nodes, host:port -------> JedisPool映射
继续回到上面,进入assignSlotsToNode(slotNums, targetNode);
public void assignSlotsToNode(List<Integer> targetSlots, HostAndPort targetNode) {
w.lock();
try {
JedisPool targetPool = setupNodeIfNotExist(targetNode);
for (Integer slot : targetSlots) {
slots.put(slot, targetPool);
}
} finally {
w.unlock();
}
}
这里设置了前面说的slots, slot ------> JedisPool的映射
这里初始化完成
执行set命令
@Override
public String set(final String key, final String value) {
return new JedisClusterCommand<String>(connectionHandler, maxAttempts) {
@Override
public String execute(Jedis connection) {
return connection.set(key, value);
}
}.run(key);
}
进入run(key);方法
public T run(String key) {
if (key == null) {
throw new JedisClusterException("No way to dispatch this command to Redis Cluster.");
}
return runWithRetries(SafeEncoder.encode(key), this.maxAttempts, false, false);
}
进入runWithRetries()
private T runWithRetries(byte[] key, int attempts, boolean tryRandomNode, boolean asking) {
if (attempts <= 0) {
throw new JedisClusterMaxRedirectionsException("Too many Cluster redirections?");
}
Jedis connection = null;
try {
if (asking) {
// TODO: Pipeline asking with the original command to make it
// faster....
connection = askConnection.get();
connection.asking();
// if asking success, reset asking flag
asking = false;
} else {
if (tryRandomNode) {
connection = connectionHandler.getConnection();
} else {
connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(key));
}
}
return execute(connection);
} catch (JedisNoReachableClusterNodeException jnrcne) {
这里有点长,截取了前面一部分
connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(key));
这里,计算key的slot,从slots获取Jedis对象
到这,基本已完成
总结一下,执行slots命令,缓存host:port --> JedisPool, slot ---->JedisPool映射。执行命令,key ---> slot ----> JedisPool ------->Jedis
Jedis Cluster源码分析的更多相关文章
- Akka源码分析-Cluster-Metrics
一个应用软件维护的后期一定是要做监控,akka也不例外,它提供了集群模式下的度量扩展插件. 其实如果读者读过前面的系列文章的话,应该是能够自己写一个这样的监控工具的.简单来说就是创建一个actor,它 ...
- Dubbo 源码分析 - 集群容错之 Cluster
1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...
- storm操作zookeeper源码分析-cluster.clj
storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中).backtype.storm.cluster定义了两个重要p ...
- Akka源码分析-Cluster-Distributed Publish Subscribe in Cluster
在ClusterClient源码分析中,我们知道,他是依托于“Distributed Publish Subscribe in Cluster”来实现消息的转发的,那本文就来分析一下Pub/Sub是如 ...
- MyCat源码分析系列之——配置信息和启动流程
更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...
- 《深入理解Spark:核心思想与源码分析》(第2章)
<深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...
- worker启动executor源码分析-executor.clj
在"supervisor启动worker源码分析-worker.clj"一文中,我们详细讲解了worker是如何初始化的.主要通过调用mk-worker函数实现的.在启动worke ...
- Tomcat源码分析——SERVER.XML文件的加载与解析
前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析的进行分析. 加载 server.xm ...
- quartz集群调度机制调研及源码分析---转载
quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的j ...
随机推荐
- bzoj1867钉子和小球
题目链接 简单$DP$ $$dp[1][1]=1(\text{显然})$$ $$map[i][j]=='*'?dp[i+1][j]+=dp[i][j]/2,dp[i+1][j+1]+=dp[i][j] ...
- luogu5212/bzoj2555 substring(后缀自动机+动态树)
对字符串构建一个后缀自动机. 每次查询的就是在转移边上得到节点的parent树中后缀节点数量. 由于强制在线,可以用动态树维护后缀自动机parent树的子树和. 注意一个玄学的优化:每次在执行连边操作 ...
- mysql 彻底解决:Incorrect string value: '\xF0\x9F\x98\xAD",...' for column 'commentContent' at row 1
彻底解决:Incorrect string value: '\xF0\x9F\x98\xAD",...' for column 'commentContent' at row 1 今天在爬取 ...
- Android 系统特有的类介绍及使用
1.Content类 在应用程序中Context的具体实现子类就是:Activity,Service,Application.可以把它理解成存储东西的仓库. 常用的上下文一般是类名.class或类名. ...
- bzoj4548: 小奇的糖果 题解
题目链接 题解 不包含所有颜色 就强制不选一个颜色 图中圆点颜色相同 矩形越大,包括的点一定不比其一小部分少 如图所示,最大矩形只有3种 离散化\(x\)坐标 然后按\(y\)排序 每次取出颜色的前驱 ...
- HDU6396 (贪心+fread 挂)
题意:初始值你有k个属性的攻击vi,有n个怪兽,每个怪兽有k种属性的血量ai,并且有k种属性的加成bi,当你的k种属性的值全部大于等于某只怪兽的k种属性的血量,你可以杀死他,并且你的攻击力vi会升级, ...
- CF F. MST Unification (最小生成树避圈法)
题意 给一个无向加权联通图,没有重边和环.在这个图中可能存在多个最小生成树(MST),你可以进行以下操作:选择某条边使其权值加一,使得MST权值不变且唯一.求最少的操作次数. 分系:首先我们先要知道为 ...
- [转] Java:对Scanner的useDelimiter()方法的疑问
[From]https://segmentfault.com/q/1010000003885362 Windows下,我们在键盘上按下Enter键,实际上输入的是回车和换行两个字符:\r\n,ASCI ...
- 牌型种数-dfs-蓝桥杯2015
牌型种数 牌型种数 小明被劫持到 X 赌城,被迫与其他 3 人玩牌. 一副扑克牌(去掉大小王牌,共 52 张),均匀发给 4 个人,每个人 13 张. 这时,小明脑子里突然冒出一个问题: 如果不考虑花 ...
- 【总结】sqlmap命令
post -u url --forms --skip(指定跳过参数) "" --technique BEUST(自选) --dbms mysql --batch --threads ...