Redis应用学习——Redis Sentinal

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1. Redis Sentinal解决主从复制的高可用问题

    1. 主从复制中存在的问题:

  • 当主节点或从节点发生故障时,必须手动进行故障转移
  • 单主节点,写能力和存储能力受限,因为只能在主节点进行写操作,而且即便有多个从节点,但这些从节点存储的数据也只是主节点的数据副本,实际上也就相当于数据只存储在主节点一台机器中。

    2. Redis Sentinal主要就是解决了主从复制中的手动故障转移所带来的麻烦,从而实现了自动故障转移,主从复制中的故障转移问题详见上一篇博客Redis应用学习(六)——主从复制,Redis Sentinal就是负责监控所有的主从节点、实现自动故障转移流程并且通知客户端故障迁移(客户端故障迁移指客户端所连接的Redis发生了故障,客户端的连接会迁移到另一个正常的Redis上),但注意,sentinel只能对主节点进行故障转移和通知客户端迁移连接,对于从节点,sentinel无法进行故障转移,也无法通知客户端进行连接迁移,只能对其做一个下线操作。

2. 认识Redis Sentinal

    1. Redis Sentinal架构:多个Sentinal节点构成一个Redis Sentinal,每个Sentinal节点都会监控着Redis中的每一个主从节点,多个Sentinal节点组成Redis Sentinal功能提供了Redis Sentinal功能的高可用型,比如当某个Sentinal节点判断Redis主节点为故障时,其他Sentinal节点也会测试该Redis主节点是否故障,进行一个类似于投票的过程,如果投票结果确定该Redis主节点确实故障,那么就会进行故障转移,而且即便某个Sentinal节点发生故障,其他的Sentinal节点也能保持Redis Sentinal功能的正常运行。而对于客户端来说,其不需要记录(直连)某个Redis节点的信息(IP地址和端口等)来进行读写,而是记录Redis Sentinal的信息(IP地址和端口等)进行交互

Redis应用学习——Redis Sentinal_第1张图片

    2. Sentinal节点:每个Sentinal节点实际上就相当于一个特殊的Redis,但是不能存储数据,默认占用端口26379,可以通过一个配置文件来修改启动的相关配置

    3. Sentinal对于主节点故障的处理:大致原理过程和手动过程类似,但是是由Sentinal自动完成的,从节点故障不会进行故障转移和通知客户端进行连接迁移

  • 多个Sentinal节点投票确定Redis主节点发生故障
  • Sentinal节点间投票选出一个Sentinal节点作为领导者
  • 通过选举出来的Sentinal领导者节点来Redis从节点中选出一个作为新的Redis主节点
  • Sentinal领导者节点将其余的Redis从节点迁移到新的Redis主节点下
  • 通知客户端主从节点变化,将连接原主节点的客户端连接到新主节点上
  • 如果发生故障的原主节点又恢复正常,那么就将恢复正常的节点变为新主节点的从节点

    4. 一个Redis Sentinal可以监控多套主从节点,详细实现下面会进行说明。

3. Redis Sentinal的安装与配置

    1. 安装过程:

  • 首先需要安装配置一套Redis主从节点,不做详细过程
  • Sentinal节点配置:在Redis解压后的安装包文件夹中,有一个Sentinal基础配置文件sentinel.conf,就是该文件对Sentinal进行配置,主要修改以下部分参数
    • port 26379:上面说过,Sentinal实际上是一个特殊的Redis,所以需要配置运行时占用端口
    • dir "":日志文件存放目录
    • logfile "port.log":生成的日志文件名
    • sentinal monitor master-name  master-ip master-port count:配置监控的Redis主节点,master-name表示要给监控的Redis主节点起一个名,必须要唯一,master-ip和master-port就是主节点的IP地址和端口,最后的count参数就是表示当count个sentinel节点都确定该主节点发生故障时,就会进行故障转移
    • sentinel down-after-milliseconds master-name 30000:master-name就某个被监控的主节点,30000这个数字就表示如果该主节点如果超过30000毫秒一直都无法ping通,那么就就会被当前sentinel节点判定为故障
    • sentinel parallel-syncs master-name count:当主节点master-name被确定为故障时,就会发生故障转移,此时某个从节点就会被sentinel设置为新的主节点,其他从节点就会重新和该新主节点建立主从关系,并且每个从节点都会和新主节点执行一次全量复制,而后面的参数count就表示是同时只允许count个从节点进行全量复制

    • sentinel failover-timeout  master-name number:进行故障转移的时间

    • daemonize yes:表示进行启动sentinel节点以守护线程方式启动

       

Redis应用学习——Redis Sentinal_第2张图片

  • 启动sentinel:通过redis-sentinel和配置文件启动,比如 ./redis-sentinel sentinel-26380.conf
  • 通过客户端连接sentinel节点,注意,sentinel节点无法执行Redis中的任何数据读写命令,只有极少命令,比如info命令就是用来获取sentinel节点自身所有的相关信息,通过这些信息可以获得sentinel是否配置启动成功Redis应用学习——Redis Sentinal_第3张图片

4. Redis Sentinal的Java客户端

    1. Sentinal的服务端与客户端的高可用:如果Sentinal不能实现主节点自动故障转移后通知客户端,那么Sentinal功能就仅仅只是实现了服务端的高可用,比如发生故障迁移后新的Redis节点IP地址和端口号不发送给客户端,那么Sentinal所实现的自动故障转移对于客户端来说相当于无效的,所以Sentinal还会在自动故障转移之后,通知客户端Redis主节点的变化,并返回信息,使得客户端也得以做出相应的改变重新连接到新的Redis主节点,那么就可以实现客户端的高可用。

    2. 通过Redis Sentinal实现客户端高可用的原理:

  • 首先遍历一边Sentinal节点集合,获取一个可用的Sentinal节点
  • 进入sentinel客户端,通过命令sentinel get-master-addr-by-name mymaster 即可获取可用Redis主节点的IP地址和端口号3a4b1d40be17387a2b0c3f2031962f1bd53.jpg
  • 在获取到Redis主节点信息时,还会自动向主节点执行一条role或role replication命令来进行验证,验证IP地址和端口号是否是真的主节点
  • 如果Redis主节点中发生了故障转移,监视主从节点的变化的sentinel就会把这些变化通知给客户端

    3. Java客户端jedis实现对Redis Sentinal监控下的Redis主节点的连接以及发送命令:jedis客户端的实现原理基本与上面所写吻合,示例代码

import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
/**
 * @ClassName:TestJedisSentinelPool
 * @Description:连接由Redis Sentinel监视的Redis主节点
 */
public class TestJedisSentinelPool {
	//注意,Dubug测试该方法时要保证linux系统的防火墙关闭(centos7关闭指令为systemctl stop firewalld)
	public static void main(String[] args) {
		/*
		 * 创建一个jedis连接池,不过该连接池创建的jedis对象所连接的Redis是被Sentinel监控的主节点
		 * masterName表示Sentinel配置中的Redis主节点名
		 * sentinels表示所有的Sentinel节点的IP地址与端口号信息
		 * poolConfig表示该连接池的相关配置参数
		 */
		String masterName="mymaster";
		Set sentinels=new LinkedHashSet();
		sentinels.add("192.168.10.128:26380");
		sentinels.add("192.168.10.128:26381");
		sentinels.add("192.168.10.128:26382");
		GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
		JedisSentinelPool pool=new JedisSentinelPool(masterName, sentinels, poolConfig);
		Jedis jedis=null;
		try {
			//注意,这里返回的jedis对象连接的仍然是Redis节点,并不是sentinel节点
			jedis=pool.getResource();
			//通过jedis就可以执行Redis的相关命令
			String response=jedis.set("user", "lisi");
			System.out.println(response);
		} catch (Exception e) {
			// 异常处理
		}finally {
			if(jedis!=null){
				//回收jedis连接对象
				jedis.close();
			}
		}
	}
}

Debug测试结果截图:

Redis应用学习——Redis Sentinal_第4张图片

    4. 模拟故障转移,观察jedis客户端的变化:要观察这种变化,需要模拟真正应用环境中,jedis客户端会不停的执行一些操作,所以更改上面的示例代码,添加一个while死循环来生成随机数,然后运行客户端测试程序,然后关闭linux系统中的Redis主节点,就可以观察到jedis中的变化,观察到服务端故障转移的过程,一旦连接的Redis主节点故障,客户端就会一直报错,但是报错一段时间之后又会停止报错继续成功执行,这个变化就是服务端自动故障转移的表现。

	//测试观察服务端自动故障转移在客户端的表现
	public void test(){
		String masterName="mymaster";
		Set sentinels=new LinkedHashSet();
		sentinels.add("192.168.10.128:26380");
		sentinels.add("192.168.10.128:26381");
		sentinels.add("192.168.10.128:26382");
		GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
		JedisSentinelPool pool=new JedisSentinelPool(masterName, sentinels, poolConfig);
		Jedis jedis=null;
		Random rand=new Random();
		String key=null;
		int value=0;
		int count=0;
		while(true){
			count++;
			try {
				value=rand.nextInt(10000);
				key="key-"+value;
				jedis=pool.getResource();
				String response=jedis.set(key, value+"");
				//每隔1秒输出一次成功执行的结果
				if(count%100==0){
					System.out.println("set "+key+":"+value+" "+response);
				}
				TimeUnit.MILLISECONDS.sleep(10);//每10毫秒执行一次循环
			} catch (Exception e) {
				// 错误提示
				System.out.println("set "+key+":"+value+" failed");
				//sentinel判定Redis主节点是否为故障,如果确定主节点故障,则进行自动故障转移
				System.out.println("服务端Redis节点异常,判断是否进行自动故障转移");
			}finally {
				if(jedis!=null){
					//回收jedis连接对象
					jedis.close();
				}
			}
		}
	}

5. Redis Sentinal的服务端高可用原理

    1. 三个定时任务:三个定时任务实现了自动故障转移中最重要的故障检测,通过以下三个定时任务就能发现出现故障的各个节点

  • 每个sentinel节点每10秒都会向自己监控的每一套Redis主从节点发送一条info命令来获取节点信息,以此来发现从节点并确认主从关系
  • 每2秒每个sentinel节点都会通过自己监控的主节点的channel(利用Redis的发布订阅功能实现)来交换信息,通过主节点中的_sentinel_:hello频道来实现各个sentinel间的交互,交互各个sentinel对主从节点甚至是其他sentinel节点的"看法"和自身信息
  • 每一秒每个sentinel节点都会对其他sentinel节点和Redis节点做ping命令操作

    2. 主观下线和客观下线:两个相关配置参数

  • sentinal monitor master-name  master-ip master-port count:sentinel的配置文件中该配置参数就是用来让sentinel节点监控一个Redis主节点及其从节点,而且通过count参数(如果超过count个sentinel节点都主观确定该Redis节点处于下线状态,那么该Redis节点就是处于客观下线)来进行判断是否该Redis节点是客观下线
  • sentinel down-after-milliseconds master-name 30000:该配置文件用于sentinel节点对Redis主节点及其从节点判断是否主观下线,如果某个sentinel节点对Redis节点超过30000毫秒一直处于无法ping通的情况,那么该节点就会被当前sentinel节点判断为主观下线

     3. 领导者选举:上面说过,在进行自动故障转移之前,sentinel节点群必须要选出一个sentinel节点作为领导者,领导者来执行故障转移操作

  • 当一个Redis节点确认下线后,每个监控该节点并对其做了主观下线的sentinel节点都会给其他每一个sentinel节点发送一个命令(sentinel is-master-down-by-addr)都希望自己成为领导者
  • 收到领导者请求命令的sentinel节点如果在此之前没有收到其他sentinel节点的请求命令,那么该请求命令就会被同意,否则被拒绝
  • 如果sentinel节点发现同意自己作为领导者的票数过半且超过count个,那么就会成为领导者执行故障转移
  • 如果有多个sentinel节点成为了领导者或没有sentinel节点成为领导者,那么将会重新选举

    4. 故障转移中的从节点选择:在故障转移中,如果主节点确认下线,那么则需要选择一个从节点来作为新的主节点,选择规则有

  • 选择从节点的salve-priority(节点优先级,可以在配置文件中进行配置,但一般不会修改)最高的节点,如果有则返回,否则执行下一条规则
  • 选择复制偏移量最大的从节点(意味着它的数据复制的完整度是最接近原来的主节点的),如果存在则返回该节点,否则执行下一条规则
  • 选择从节点中run-id最小的,这意味着该节点是运行时间最久的(或者说启动最早的)。

6. Redis Sentinal的常见运维

    1. 节点运维:就是节点的手动上线和下线操作,针对的目标是Redis主节点、从节点和sentinel节点。

    2. 节点运维解决的问题:当下面这些问题发生时,应该由手动执行节点的上下线操作来进行一个故障转移

  • 机器下线:由于某种原因导致机器不能在使用(比如机器使用时间超过保质期等)
  • 机器性能不足:例如CPU、内存、硬盘等
  • 节点自身运行故障:例如系统宕机等

    3. 对于主节点的下线:手动指定一个sentinel节点作为领导者,并对指定的Redis主节点进行故障转移:sentinel failover master-name,在某个sentinel节点的客户端运行该命令就是指定该sentinel节点为领导者,并且对其所监控的master-name节点进行故障转移

    4. 对于从节点的下线:首先需要确定是临时下线(暂时关闭该节点,之后还会进行重启)还是永久下线(不再使用这台机器作为一个从节点提供服务),下线后是否需要进行一些清理工作,比如从节点的一些配置,AOF、RDB以及日志文件的清理,比如如果要临时下线来启动一下其他进程,要保证从节点的相关配置不会干扰到新进程的运行。此外,从节点的下线还要考虑读写分离的问题。

    5. 对于sentinel节点的下线:与从节点类似

    6. 节点上线:对于Redis主节点的上线,同样使用sentinel failover命令切换主节点即可;而对于从节点的上线使用slaveof命令将该从节点关联到主节点即可,sentinel会自动感知;对于sentinel节点的上线,只需要写好配置文件直接启动即可。

7. Redis Sentinal中高可用读写分离的实现

    1. Redis Sentinal中读写分离存在的问题:客户端连接到一个从节点只进行读操作,但如果该从节点下线后,客户端无法获知下线信息,也无法将连接转移到另一个从节点上,因为sentinel只对从节点进行一个主观下线操作,但不会通知客户端从节点的下线信息,也就无法做客户端连接迁移(将连接原从节点的客户端转移到另一个从节点上)。

    2. 要实现高可用的读写分离,客户端就要知道从节点变化的三个消息:

  • 切换为主节点,也就是从节点晋升为主节点
  • 切换为从节点,主节点变为从节点
  • 从节点下线

    3. 实现原理:连接从节点的客户端必须能感知到所有从节点的变化,也就是上面的三条消息,但是Java客户端里并没有提供这种从节点的读写分离实现,需要我们自己去实现,可以参考JedisSentinelPool的实现来手动编写一个从节点高可用连接池。但很明显,sentinel环境下实现读写分离的高可用非常复杂,对于性能提高、容量扩展的时候,这种方式是比较复杂的,所以,推荐使用集群(redis-cluster)。

Redis应用学习——Redis Sentinal_第5张图片

转载于:https://my.oschina.net/ProgramerLife/blog/2361809

你可能感兴趣的:(Redis应用学习——Redis Sentinal)