原文链接:《Jedis 源码分析》http://www.ytbean.com/posts/jedis-source-walk/
概述
Jedis是Redis官方推荐的Java客户端,更多Redis的客户端可以参考Redis官网客户端列表。
JedisSentinelPool
简介
Redis-Sentinel作为官方推荐的HA解决方案,Jedis也在客户端角度实现了对Sentinel的支持,主要实现在JedisSentinelPool.java
这个类中,下文会分析这个类的实现。
属性
JedisSentinelPool类里有以下的属性:
//基于apache的commom-pool2的对象池配置
protected GenericObjectPoolConfig poolConfig;
//超时时间,默认是2000
protected int timeout = Protocol.DEFAULT_TIMEOUT;
//sentinel的密码
protected String password;
//redis数据库的数目
protected int database = Protocol.DEFAULT_DATABASE;
//master监听器,当master的地址发生改变时,会触发这些监听者
protected Set<MasterListener> masterListeners = new HashSet<MasterListener>();
protected Logger log = Logger.getLogger(getClass().getName());
//Jedis实例创建工厂
private volatile JedisFactory factory;
//当前的master,HostAndPort是一个简单的包装了ip和port的模型类
private volatile HostAndPort currentHostMaster;
构造器
构造器的代码如下:
public JedisSentinelPool(String masterName, Set<String> sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database) {
this.poolConfig = poolConfig;
this.timeout = timeout;
this.password = password;
this.database = database;
HostAndPort master = initSentinels(sentinels, masterName);
initPool(master);
}
构造器一开始对实例变量进行赋值,参数sentinels是客户端所需要打交道的Redis-Sentinel,允许有多个,用一个集合来盛装。
然后通过initSentinels方法与sentinel沟通后,确定当前sentinel所监视的master是哪一个。然后通过master来创建好对象池,以便后续从对象池中取出一个Jedis实例,来对master进行操作。
initSentinels方法
initSentinels方法的代码如下所示,我加了一些注释:
private HostAndPort initSentinels(Set<String> sentinels, final String masterName) {
HostAndPort master = null;
boolean sentinelAvailable = false;
log.info("Trying to find master from available Sentinels...");
// 有多个sentinels,遍历这些个sentinels
for (String sentinel : sentinels) {
// host:port表示的sentinel地址转化为一个HostAndPort对象。
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
log.fine("Connecting to Sentinel " + hap);
Jedis jedis = null;
try {
// 连接到sentinel
jedis = new Jedis(hap.getHost(), hap.getPort());
// 根据masterName得到master的地址,返回一个list,host= list[0], port =
// list[1]
List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
// connected to sentinel...
sentinelAvailable = true;
if (masterAddr == null || masterAddr.size() != 2) {
log.warning("Can not get master addr, master name: " + masterName
+ ". Sentinel: " + hap + ".");
continue;
}
master = toHostAndPort(masterAddr);
log.fine("Found Redis master at " + master);
// 如果在任何一个sentinel中找到了master,不再遍历sentinels
break;
} catch (JedisConnectionException e) {
log.warning("Cannot connect to sentinel running @ " + hap
+ ". Trying next one.");
} finally {
// 关闭与sentinel的连接
if (jedis != null) {
jedis.close();
}
}
}
// 到这里,如果master为null,则说明有两种情况,一种是所有的sentinels节点都down掉了,一种是master节点没有被存活的sentinels监控到
if (master == null) {
if (sentinelAvailable) {
// can connect to sentinel, but master name seems to not
// monitored
throw new JedisException("Can connect to senti