Redis应用学习——Redis Sentinal

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

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

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

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

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

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

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

  • 多个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个从节点进行全量复制dom

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

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

       

  • 启动sentinel:经过redis-sentinel和配置文件启动,好比 ./redis-sentinel sentinel-26380.conf
  • 经过客户端链接sentinel节点,注意,sentinel节点没法执行Redis中的任何数据读写命令,只有极少命令,好比info命令就是用来获取sentinel节点自身全部的相关信息,经过这些信息能够得到sentinel是否配置启动成功

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地址和端口号
  • 在获取到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<String> sentinels=new LinkedHashSet<String>();
		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测试结果截图:

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

//测试观察服务端自动故障转移在客户端的表现
	public void test(){
		String masterName="mymaster";
		Set<String> sentinels=new LinkedHashSet<String>();
		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)。

相关文章
相关标签/搜索