Redis哨兵原理总结(四):源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jingzi123456789/article/details/84762173

目录

本博文主要总结关于哨兵的一些理论知识,主要关注点有一下几个方面:

一、哨兵解决了什么问题?

二、哨兵是如何解决“问题一”的?

三、如何使用哨兵?

四、Redis Sentinel客户端实现的原理是什么?Java如何操作Redis Sentinel?

四、Redis Sentinel客户端实现的原理是什么?Java如何操作Redis Sentinel?

首先,看JedisSentinelPool的构造函数。



Jedis Version:2.9.0

sentinel构造函数:

/**
  *Set<String> sentinels:哨兵集合,格式为IP:PORT
  */
public JedisSentinelPool(String masterName, Set<String> sentinels, GenericObjectPoolConfig poolConfig, int timeout, String password) {
	this(masterName, sentinels, poolConfig, timeout, password, 0);
}
	
	
public JedisSentinelPool(String masterName, Set<String> sentinels, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, int database, String clientName) {
	this.connectionTimeout = 2000;
	this.soTimeout = 2000;
	this.database = 0;
	this.masterListeners = new HashSet();
	this.log = Logger.getLogger(this.getClass().getName());
	this.poolConfig = poolConfig;
	this.connectionTimeout = connectionTimeout;
	this.soTimeout = soTimeout;
	this.password = password;
	this.database = database;
	this.clientName = clientName;
	HostAndPort master = this.initSentinels(sentinels, masterName);
	this.initPool(master);
}

 再看,如何初始化哨兵的,initSentinels(sentinels, masterName)。

private HostAndPort initSentinels(Set<String> sentinels, String masterName) {
	HostAndPort master = null;
	boolean sentinelAvailable = false;
	this.log.info("Trying to find master from available Sentinels...");
	Iterator var5 = sentinels.iterator();

	String sentinel;
	HostAndPort hap;
	while(var5.hasNext()) {
		sentinel = (String)var5.next();
		hap = HostAndPort.parseString(sentinel);
		this.log.fine("Connecting to Sentinel " + hap);
		Jedis jedis = null;

		try {
			jedis = new Jedis(hap.getHost(), hap.getPort());
			List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
			sentinelAvailable = true;
			if (masterAddr != null && masterAddr.size() == 2) {
				master = this.toHostAndPort(masterAddr);
				this.log.fine("Found Redis master at " + master);
				break;
			}

			this.log.warning("Can not get master addr, master name: " + masterName + ". Sentinel: " + hap + ".");
		} catch (JedisException var13) {
			this.log.warning("Cannot get master address from sentinel running @ " + hap + ". Reason: " + var13 + ". Trying next one.");
		} finally {
			if (jedis != null) {
				jedis.close();
			}

		}
	}

	if (master == null) {
		if (sentinelAvailable) {
			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...");
		}
	} else {
		this.log.info("Redis master running at " + master + ", starting Sentinel listeners...");
		var5 = sentinels.iterator();

		while(var5.hasNext()) {
			sentinel = (String)var5.next();
			// 获取哨兵的IP和PORT
			hap = HostAndPort.parseString(sentinel);
			// 创建一个线程创建监听器,对每个哨兵进行监听
			JedisSentinelPool.MasterListener masterListener = new JedisSentinelPool.MasterListener(masterName, hap.getHost(), hap.getPort());
			// 设置为守护线程
			masterListener.setDaemon(true);
			this.masterListeners.add(masterListener);
			masterListener.start();
		}
		return master;
	}
}
private HostAndPort initSentinels(Set<String> sentinels, String masterName) {
	HostAndPort master = null;
	boolean sentinelAvailable = false;
	this.log.info("Trying to find master from available Sentinels...");
	Iterator var5 = sentinels.iterator();

	String sentinel;
	HostAndPort hap;
	while(var5.hasNext()) {
		sentinel = (String)var5.next();
		hap = HostAndPort.parseString(sentinel);
		this.log.fine("Connecting to Sentinel " + hap);
		Jedis jedis = null;

		try {
			jedis = new Jedis(hap.getHost(), hap.getPort());
			List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
			sentinelAvailable = true;
			if (masterAddr != null && masterAddr.size() == 2) {
				master = this.toHostAndPort(masterAddr);
				this.log.fine("Found Redis master at " + master);
				break;
			}

			this.log.warning("Can not get master addr, master name: " + masterName + ". Sentinel: " + hap + ".");
		} catch (JedisException var13) {
			this.log.warning("Cannot get master address from sentinel running @ " + hap + ". Reason: " + var13 + ". Trying next one.");
		} finally {
			if (jedis != null) {
				jedis.close();
			}

		}
	}

	if (master == null) {
		if (sentinelAvailable) {
			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...");
		}
	} else {
		this.log.info("Redis master running at " + master + ", starting Sentinel listeners...");
		var5 = sentinels.iterator();

		while(var5.hasNext()) {
			sentinel = (String)var5.next();
			// 获取哨兵的IP和PORT
			hap = HostAndPort.parseString(sentinel);
			// 创建一个线程创建监听器,对每个哨兵进行监听
			JedisSentinelPool.MasterListener masterListener = new JedisSentinelPool.MasterListener(masterName, hap.getHost(), hap.getPort());
			// 设置为守护线程
			masterListener.setDaemon(true);
			this.masterListeners.add(masterListener);
			masterListener.start();
		}
		return master;
	}
}

对master监听的监听器的源码,创建一个线程监听master。

protected class MasterListener extends Thread {
	protected String masterName;
	protected String host;
	protected int port;
	protected long subscribeRetryWaitTimeMillis;
	protected volatile Jedis j;
	protected AtomicBoolean running;

	protected MasterListener() {
		this.subscribeRetryWaitTimeMillis = 5000L;
		this.running = new AtomicBoolean(false);
	}

	public MasterListener(String masterName, String host, int port) {
		super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port));
		this.subscribeRetryWaitTimeMillis = 5000L;
		this.running = new AtomicBoolean(false);
		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;
	}

	public void run() {
		this.running.set(true);

		while(this.running.get()) {
			this.j = new Jedis(this.host, this.port);

			try {
				if (!this.running.get()) {
					break;
				}
				// 订阅+switch-master频道。
				// 如果哨兵完成了主从切换,会在+switch-master频道发布这行信息:pmaster 172.16.0.149 6379 172.16.0.34 6379
				// 然后对消息“pmaster 172.16.0.149 6379 172.16.0.34 6379”以“ ”分割得到一个String类型的数组,数组的第4个元素就是新的master的IP,第5个元素就是新的master的port,第1个元素就是新的master的名称。
				this.j.subscribe(new JedisPubSub() {
					public void onMessage(String channel, String message) {
						JedisSentinelPool.this.log.fine("Sentinel " + MasterListener.this.host + ":" + MasterListener.this.port + " published: " + message + ".");
						String[] switchMasterMsg = message.split(" ");
						if (switchMasterMsg.length > 3) {
							if (MasterListener.this.masterName.equals(switchMasterMsg[0])) {
								JedisSentinelPool.this.initPool(JedisSentinelPool.this.toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4])));
							} else {
								JedisSentinelPool.this.log.fine("Ignoring message on +switch-master for master name " + switchMasterMsg[0] + ", our master name is " + MasterListener.this.masterName);
							}
						} else {
							JedisSentinelPool.this.log.severe("Invalid message received on Sentinel " + MasterListener.this.host + ":" + MasterListener.this.port + " on channel +switch-master: " + message);
						}
					}
				}, new String[]{"+switch-master"});
			} catch (JedisConnectionException var8) {
				if (this.running.get()) {
					JedisSentinelPool.this.log.log(Level.SEVERE, "Lost connection to Sentinel at " + this.host + ":" + this.port + ". Sleeping 5000ms and retrying.", var8);

					try {
						Thread.sleep(this.subscribeRetryWaitTimeMillis);
					} catch (InterruptedException var7) {
						JedisSentinelPool.this.log.log(Level.SEVERE, "Sleep interrupted: ", var7);
					}
				} else {
					JedisSentinelPool.this.log.fine("Unsubscribing from Sentinel at " + this.host + ":" + this.port);
				}
			} finally {
				this.j.close();
			}
		}
	}
}

Redis消息订阅与发布:

客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器:

你不可以使用 PUBLISH 命令向这个服务器发送信息, 但你可以用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令, 通过订阅给定的频道来获取相应的事件提醒。

一个频道能够接收和这个频道的名字相同的事件。

比如说, 名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。通过执行 PSUBSCRIBE * 命令可以接收所有事件信息。
    +switch-master <master name> <oldip> <oldport> <newip> <newport> :配置变更,主服务器的 IP 和地址已经改变。

这是绝大多数外部用户都关心的信息。

可以看出,我们使用Sentinel命令和发布订阅两种机制就能很好的实现和客户端的集成整合:使用get-master-addr-by-name和slaves指令可以获取当前的Master和Slaves的地址和信息;而当发生故障转移时,即Master发生切换,可以通过订阅的+switch-master事件获得最新的Master信息。

——————————————————Redis哨兵原理总结完结——————————————————

订阅与发布的基本原理:
https://redisbook.readthedocs.io/en/latest/feature/pubsub.html

猜你喜欢

转载自blog.csdn.net/jingzi123456789/article/details/84762173