共计 6452 个字符,预计需要花费 17 分钟才能阅读完成。
在 Redis 主从复制架构中,如果 master 出现了故障,则需要人工将 slave 提升为 master,同时,通知应用侧更新 master 的地址。这样方式比较低效,对应用侧影响较大。
为了解决这个问题,Redis 2.8 中推出了自己的高可用方案 Redis Sentinel。
Redis Sentinel 架构图如下:
默认情况下,每个 Sentinel 节点会以每秒一次的频率对 Redis 节点和其它的 Sentinel 节点发送 PING 命令,并通过节点的回复来判断节点是否在线。
如果在 down-after-millisecondes 毫秒内,没有收到有效的回复,则会判定该节点为主观下线。
如果该节点为 master,则该 Sentinel 节点会通过 sentinel is-master-down-by-addr 命令向其它 sentinel 节点询问对该节点的判断,如果超过 <quorum> 个数的节点判定 master 不可达,则该 sentinel 节点会将 master 判断为客观下线。
这个时候,各个 Sentinel 会进行协商,选举出一个领头 Sentinel,由该领头 Sentinel 对 master 节点进行故障转移操作。
故障转移包含如下三个操作:
1. 在所有的 slave 服务器中,挑选出一个 slave,并将其转换为 master。
2. 让其它 slave 服务器,改为复制新的 master。
3. 将旧 master 设置为新 master 的 slave,这样,当旧的 master 重新上线时,它会成为新 master 的 slave。
以上的所有操作对业务都是透明的,当新的 master 上线后,Sentinel 会自动将这个变化实时通知给业务方。
那么,业务侧又该如何配置,才能扑捉到这个变化呢?
其实,这个主要取决于 Redis 客户端工具是否支持 Redis Sentinel,对于支持的客户端工具来说,如 Jedis,
只需将连接字符串设置为 Sentinel 地址即可。
下面,给出了一个测试代码,并模拟了 master 发生故障,业务侧是如何处理的?
代码如下:
package com.victor_02;
import Java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
public class JedisSentinelTest {public static void main(String[] args) throws Exception {// TODO Auto-generated method stub
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.244.10:26379");
sentinels.add("192.168.244.10:26380");
sentinels.add("192.168.244.10:26381");
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = null;
while (true) {Thread.sleep(1000);
try {jedis = jedisSentinelPool.getResource();
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String format_now = dateFormat.format(now);
jedis.set("hello", "world");
String value = jedis.get("hello");
System.out.println(format_now + '' + value);
} catch (Exception e) {System.out.println(e);
} finally {if (jedis != null)
try {jedis.close();
} catch (Exception e) {System.out.println(e);
}
}
}
}
}
模拟故障:
# ./redis-cli -p 6380
127.0.0.1:6380> shutdown
上述代码的输出如下:
四月 16, 2017 10:39:44 下午 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Trying to find master from available Sentinels...
四月 16, 2017 10:39:44 下午 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Redis master running at 192.168.244.10:6380, starting Sentinel listeners...
四月 16, 2017 10:39:44 下午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.244.10:6380
2017/04/16 22:39:45 world
2017/04/16 22:39:46 world
2017/04/16 22:39:47 world
2017/04/16 22:39:48 world
2017/04/16 22:39:49 world
2017/04/16 22:39:50 world
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Software caused connection abort: recv failed
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
四月 16, 2017 10:40:21 下午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.244.10:6381
四月 16, 2017 10:40:21 下午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.244.10:6381
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
2017/04/16 22:40:22 world
2017/04/16 22:40:23 world
2017/04/16 22:40:24 world
2017/04/16 22:40:25 world
2017/04/16 22:40:26 world
从上述输出可以看出,在 master 发生故障前,业务侧最后一次正常处理(22:39:50),到再次正常处理是(22:40:22),中间经过了 32s。
而其中 30s 被用来判断 master 节点是否主观下线(由 down-after-milliseconds 来指定),整个切换的过程还是比较高效的。
下面关于 Redis 的文章您也可能喜欢,不妨参考下:
Ubuntu 14.04 下 Redis 安装及简单测试 http://www.linuxidc.com/Linux/2014-05/101544.htm
Redis 主从复制基本配置 http://www.linuxidc.com/Linux/2015-03/115610.htm
CentOS 7 下 Redis 的安装与配置 http://www.linuxidc.com/Linux/2017-02/140363.htm
Ubuntu 14.04 安装 Redis 与简单配置 http://www.linuxidc.com/Linux/2017-01/139075.htm
Ubuntu 16.04 环境中安装 PHP7.0 Redis 扩展 http://www.linuxidc.com/Linux/2016-09/135631.htm
Redis 单机 & 集群离线安装部署 http://www.linuxidc.com/Linux/2017-03/141403.htm
CentOS 7.0 安装 Redis 3.2.1 详细过程和使用常见问题 http://www.linuxidc.com/Linux/2016-09/135071.htm
Ubuntu 16.04 环境中安装 PHP7.0 Redis 扩展 http://www.linuxidc.com/Linux/2016-09/135631.htm
Ubuntu 15.10 下 Redis 集群部署文档 http://www.linuxidc.com/Linux/2016-06/132340.htm
Redis 实战 中文 PDF http://www.linuxidc.com/Linux/2016-04/129932.htm
Redis 热迁移实战总结 http://www.linuxidc.com/Linux/2017-02/141083.htm
Redis3.0 配置文件详解 http://www.linuxidc.com/Linux/2017-03/141369.htm
本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-05/143433.htm