Redis实践(八)-Sentinal
2018-08-28 本文已影响211人
紫霞等了至尊宝五百年
1
![](https://img.haomeiwen.com/i4685968/05b82cafebdaae3d.png)
2 主从复制高可用?
![](https://img.haomeiwen.com/i4685968/7a641574ce6f2f61.png)
![](https://img.haomeiwen.com/i4685968/a04dbe1017cd96e5.png)
![](https://img.haomeiwen.com/i4685968/0561b0fe8f877b64.png)
3 Redis Sentinel 架构
![](https://img.haomeiwen.com/i4685968/306c1e7c4eeed31b.png)
![](https://img.haomeiwen.com/i4685968/0cd2a496cdd99352.png)
![](https://img.haomeiwen.com/i4685968/f6d61d450da430ae.png)
4 安装与配置
![](https://img.haomeiwen.com/i4685968/30cba67a1b8d5c30.png)
![](https://img.haomeiwen.com/i4685968/679080c6d277fded.png)
![](https://img.haomeiwen.com/i4685968/cc2e84ad0489ff40.png)
![](https://img.haomeiwen.com/i4685968/cb4024c528ac91b7.png)
![](https://img.haomeiwen.com/i4685968/67d1ff48c0d1116f.png)
5 安装与演示
![](https://img.haomeiwen.com/i4685968/6c474596767eb330.png)
![](https://img.haomeiwen.com/i4685968/3bb0bb58a8b8552d.png)
![](https://img.haomeiwen.com/i4685968/e2cb1c981b223ad4.png)
![](https://img.haomeiwen.com/i4685968/f245c1dbe952cf23.png)
![](https://img.haomeiwen.com/i4685968/ffa5966fd4bacfd9.png)
![](https://img.haomeiwen.com/i4685968/0da7504105611cb0.png)
6 客户端
![](https://img.haomeiwen.com/i4685968/72fcffa91846101a.png)
![](https://img.haomeiwen.com/i4685968/dfab1f76a34c1408.png)
![](https://img.haomeiwen.com/i4685968/56a3787002418c40.png)
![](https://img.haomeiwen.com/i4685968/9a7b2e13f35c1c1d.png)
![](https://img.haomeiwen.com/i4685968/c9349701a2409121.png)
![](https://img.haomeiwen.com/i4685968/0810ddda40bc175e.png)
![](https://img.haomeiwen.com/i4685968/5f960a003f8013b7.png)
![](https://img.haomeiwen.com/i4685968/0e917792d9448f91.png)
![](https://img.haomeiwen.com/i4685968/af0c7e10a81236a6.png)
11 三个定时任务
![](https://img.haomeiwen.com/i4685968/ceef848c86fa60c8.png)
![](https://img.haomeiwen.com/i4685968/52d8c597d3388cfb.png)
![](https://img.haomeiwen.com/i4685968/07da0066a14c7321.png)
![](https://img.haomeiwen.com/i4685968/31ac7fb5a75dfbcb.png)
12 主观下线和客观下线
![](https://img.haomeiwen.com/i4685968/2c99a25f9b733d81.png)
13 领导者选举
![](https://img.haomeiwen.com/i4685968/81f1730acc8450e9.png)
![](https://img.haomeiwen.com/i4685968/b250ba6d57c35be6.png)
14 故障转移
![](https://img.haomeiwen.com/i4685968/fed799d9f4f7ef2b.png)
![](https://img.haomeiwen.com/i4685968/b9a0c4a3f46bb978.png)
15 常见开发运维问题-目录
![](https://img.haomeiwen.com/i4685968/0db1b616051726c6.png)
16 节点运维
![](https://img.haomeiwen.com/i4685968/1b3c8d1446ff02cd.png)
![](https://img.haomeiwen.com/i4685968/f777adc65f8c89d1.png)
![](https://img.haomeiwen.com/i4685968/9fbb841ea1620ede.png)
![](https://img.haomeiwen.com/i4685968/e7906ab64ff26b60.png)
17 高可用的读写分离
看一下JedisSentinelPool
的实现
![](https://img.haomeiwen.com/i4685968/b19873f0f2a44fc1.png)
![](https://img.haomeiwen.com/i4685968/6a170ac4761d7e8a.png)
![](https://img.haomeiwen.com/i4685968/02ae32cdf67b7f4f.png)
![](https://img.haomeiwen.com/i4685968/a2a7b679d8f0a85c.png)
private HostAndPort initSentinels(Set<String> sentinels, final String masterName) {
HostAndPort master = null;
boolean sentinelAvailable = false;
log.info("Trying to find master from available Sentinels...");
// 遍历所有 Sentinel 节点
for (String sentinel : sentinels) {
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
log.fine("Connecting to Sentinel " + hap);
Jedis jedis = null;
try {
// 找到一个可运行的,并用jedis连接上去
jedis = new Jedis(hap.getHost(), hap.getPort());
// 通过 mastername 区分 sentinel 节点,得到其地址
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);
// 找到可用节点,结束遍历,跳出循环
break;
} catch (JedisException e) {
// resolves #1036, it should handle JedisException there's another chance
// of raising JedisDataException
log.warning("Cannot get master address from sentinel running @ " + hap + ". Reason: " + e
+ ". Trying next one.");
} finally {
if (jedis != null) {
jedis.close();
}
}
}
//无可用节点,抛异常
if (master == null) {
if (sentinelAvailable) {
// can connect to sentinel, but master name seems to not
// monitored
throw new JedisException("Can connect to sentinel, but " + masterName
+ " seems to be not monitored...");
} else {
throw new JedisConnectionException("All sentinels down, cannot determine where is "
+ masterName + " master is running...");
}
}
log.info("Redis master running at " + master + ", starting Sentinel listeners...");
//当拿到master节点后,所要做的就是订阅那个消息,客户端只需订阅该频道即可
for (String sentinel : sentinels) {
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
// whether MasterListener threads are alive or not, process can be stopped
masterListener.setDaemon(true);
masterListeners.add(masterListener);
masterListener.start();
}
以下为监听线程类
protected class MasterListener extends Thread {
protected String masterName;
protected String host;
protected int port;
protected long subscribeRetryWaitTimeMillis = 5000;
protected volatile Jedis j;
protected AtomicBoolean running = new AtomicBoolean(false);
protected MasterListener() {
}
public MasterListener(String masterName, String host, int port) {
super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port));
this.masterName = masterName;
this.host = host;
this.port = port;
}
public MasterListener(String masterName, String host, int port,
long subscribeRetryWaitTimeMillis) {
this(masterName, host, port);
this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis;
}
@Override
public void run() {
running.set(true);
while (running.get()) {
j = new Jedis(host, port);
try {
// double check that it is not being shutdown
if (!running.get()) {
break;
}
j.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
log.fine("Sentinel " + host + ":" + port + " published: " + message + ".");
String[] switchMasterMsg = message.split(" ");
if (switchMasterMsg.length > 3) {
if (masterName.equals(switchMasterMsg[0])) {
initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4])));
} else {
log.fine("Ignoring message on +switch-master for master name "
+ switchMasterMsg[0] + ", our master name is " + masterName);
}
} else {
log.severe("Invalid message received on Sentinel " + host + ":" + port
+ " on channel +switch-master: " + message);
}
}
}, "+switch-master");
} catch (JedisConnectionException e) {
if (running.get()) {
log.log(Level.SEVERE, "Lost connection to Sentinel at " + host + ":" + port
+ ". Sleeping 5000ms and retrying.", e);
try {
Thread.sleep(subscribeRetryWaitTimeMillis);
} catch (InterruptedException e1) {
log.log(Level.SEVERE, "Sleep interrupted: ", e1);
}
} else {
log.fine("Unsubscribing from Sentinel at " + host + ":" + port);
}
} finally {
j.close();
}
}
}
public void shutdown() {
try {
log.fine("Shutting down listener on " + host + ":" + port);
running.set(false);
// This isn't good, the Jedis object is not thread safe
if (j != null) {
j.disconnect();
}
} catch (Exception e) {
log.log(Level.SEVERE, "Caught exception while shutting down: ", e);
}
}
}
![](https://img.haomeiwen.com/i4685968/98c3f46eb28f4406.png)
![](https://img.haomeiwen.com/i4685968/b981be9b41f08a89.png)
![](https://img.haomeiwen.com/i4685968/f78f3e235b94bbf3.png)
由于Redis Sentinel只会对主节点进行故障转移,对从节点采取主观的下线,所以需要自定义一个客户端来监控对应的事件
![](https://img.haomeiwen.com/i4685968/20b7bcc64af21ae1.png)
![](https://img.haomeiwen.com/i4685968/9b7794ca52e412e2.png)
18 总结
![](https://img.haomeiwen.com/i4685968/60883ba8eeacbbc9.png)
![](https://img.haomeiwen.com/i4685968/28d299b6356fd144.png)
![](https://img.haomeiwen.com/i4685968/c11a03ead788b0d8.png)