1
目录
2 主从复制高可用?
故障出现主节点挂掉
主从复制-mster宕掉故障处理
3 Redis Sentinel 架构
可监控多套
4 安装与配置
安装与配置
Redis 主节点
Redis 从节点
Sentinel 主要配置
5 安装与演示
主节点配置
快速生成从节点配置文件
重定向
打印检查配置文件
启动
6 客户端
客户端
直连?
客户端实现基本原理-1
客户端实现基本原理-2
客户端实现基本原理-3 验证
客户端实现基本原理-4 通知(发布订阅))
客户端接入流程
Jedis
11 三个定时任务
三个定时任务
每 10s info
第2个监控任务,每 2s 发布订阅
第3个监控任务,每 1s PING
12 主观下线和客观下线
主观下线和客观下线
13 领导者选举
领导者选举
选举实例
14 故障转移
领导者节点完成
选择合适的slave节点
15 常见开发运维问题-目录
16 节点运维
主节点
节点下线
节点上线
17 高可用的读写分离
看一下JedisSentinelPool的实现
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);
}
}
}感知到主从切换时,接收消息并重新初始化连接池
从节点的作用
由于Redis Sentinel只会对主节点进行故障转移,对从节点采取主观的下线,所以需要自定义一个客户端来监控对应的事件
三个消息
高可用读写分离
18 总结
作者:芥末无疆sss
链接:https://www.jianshu.com/p/6dad9cc2323e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。