主从复制/读写分离策略有个致命的问题,master挂掉且不可恢复了怎么办?
有一种方式是手动进行主从切换。具体的操作是,先在slave上执行slaveof no one
命令把自己变成新的master,然后手动的把其他节点连接到这个新的master上。
显然,这种方式费时费力,还会造成一段时间内服务不可用。而哨兵模式,可以自动的、透明的、健壮的完成主从切换的任务。
哨兵是一个独立运行的的进程,它监控着所有的Redis服务器。
当然,一个哨兵可能还会出问题,因此可以设置多个哨兵,哨兵之间也能相互监控。
假设master宕机,哨兵x先检测到这个问题,但并不会立即进行failover(故障切换)操作,仅仅是哨兵x认为master不可用,称为主观下线;当其他的哨兵也发现master不可用,并且达到一定的数量时,那么便会通过投票选举出新的master,进行failover(故障切换)操作;切换成功后,新的master通过发布/订阅模式,通知其他slave修改配置,称为客观下线。
首先明确,redis-sentinel.conf和redis.conf是两个配置文件。
下面,我们来瞧瞧redis-sentinel.conf中究竟有哪些配置项吧 >_<!!!
# 哨兵实例运行的端口,默认为26379
port 26379
# 哨兵服务的临时工作文件夹,默认为/tmp,要保证有写入权限
dir /tmp
# 哨兵监控的主节点
# 后边紧跟的数字的含义是,有多少个哨兵认为主节点主观下线,则对其进行客观下线处理
sentinel monitor mymaster 127.0.0.1 6379 2
# redis开启密码服务后,所有连接redis服务器的客户端都要提供密码,哨兵也不例外
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后,主节点未应答哨兵,则该哨兵认为其主观下线
sentinel down-after-milliseconds mymaster 30000
# 指定发生故障转移后,最多有多少个slave同时与新的master同步,类似于并行的数量限制
# 这个数字越小,则故障转移完成需要的时间越长;这个数字越大,对网络要求越高,并且意味着当前有多个slave不可用!
sentinel parallel-syncs mymaster 1
# 指定故障切换允许话费的最大毫秒数,超时则失败
sentinel failover-timeout mymaster 180000
# 指定某一事件发生后需要执行的脚本
# 比如,当系统运行不正常时,该脚本发送一个邮件通知相关人员
sentinel notification-script mymaster /var/redis/notify.sh
# 主节点切换后,该脚本将"主节点切换"的相关信息通知给客户端
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
public class sentinelTest {
@Test
public void test() {
// 连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMaxWaitMillis(2000);
// 哨兵信息
Set<String> sentinels = new HashSet<>(Arrays.asList("127.0.0.1.26379", "127.0.0.2.26379", "127.0.0.3.26379"));
// 创建连接池
JedisSentinelPool pool = new JedisSentinelPool("master", sentinels, config);
// 获取客户端并使用
Jedis jedis = pool.getResource();
jedis.set("loli", "alice");
System.out.println(jedis.get("loli"));
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="100"/>
<property name="maxIdle" value="50"/>
<property name="maxWaitMillis" value="2000"/>
bean>
<bean name="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
<bean name="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean name="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="master"/>
bean>
property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="26379"/>
bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.2"/>
<constructor-arg name="port" value="26379"/>
bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="127.0.0.3"/>
<constructor-arg name="port" value="26379"/>
bean>
set>
property>
bean>
<bean name="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="sentinelConfig" ref="sentinelConfiguration"/>
<constructor-arg name="poolConfig" ref="poolConfig"/>
bean>
<bean name="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultSerializer" ref="jdkSerializationRedisSerializer"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="stringRedisSerializer"/>
bean>
beans>
☘️ 玩转Redis专题!
不来博客里瞧一瞧吗?