一、用主从复制实现高可用?
1、为主提供备份
2、为主提供分流,减轻主压力
3、如果主出现了问题,故障转移基本上需要手工完成,或者是脚本
slaveof no one #slava 变成主
[slaveof newMasterIp port] # 多个从节点手动执行
4、写能力和存储能力受限(只能在一个主节点上进行写和存储)
二、哨兵的架构说明
Redis 哨兵架构 Redis Sentinal故障转移2 Redis Sentinal故障转移3 一个哨兵实现可以监控多个主从Redis三、安装配置
实战配置目标架构0. 注意事项
1)配置开启主从节点
2)配置开启sentinel监控主节点(sentinel是特殊的Redis)
3)实际应该多台机器
4)详细配置节点
1. Redis主节点配置文件:
redis-server redis-7000.conf
port 7000
daemonize yes
pidfile "/var/run/redis-7000.pid"
logfile "redis-7000.log"
dbfilename "dump-7000.rdb"
dir "/usr/local/redis/data/"
#省略了RDB配置
2. Redis从节点1配置文件
redis-server redis-7001.conf
port 7001
daemonize yes
pidfile "/var/run/redis-7001.pid"
logfile "redis-7001.log"
dbfilename "dump-7001.rdb"
dir "/usr/local/redis/data/"
#旧版本用slaveof
#slaveof 127.0.0.1 7000
replicaof 127.0.0.1 7000
3. Redis从节点2配置文件
redis-server redis-7002.conf
port 7002
daemonize yes
pidfile "/var/run/redis-7002.pid"
logfile "redis-7002.log"
dbfilename "dump-7002.rdb"
dir "/usr/local/redis/data/"
#旧版本用slaveof
#slaveof 127.0.0.1 7000
replicaof 127.0.0.1 7000
4. 在redis-5.0.4根目录下找到sentinel.conf
#配置哨兵需要监控的主节点ip和端口,最后的2代表:
#如果有2个哨兵主观认为主节点down了,那么就客观认为主节点down掉了,
#开始发起投票选举新主节点的操作。多个主节点配置多个。
sentinel monitor mymaster 192.167.3.145 7000 2
#配置哨兵连接主节点的认证密码。(主节点配置的requirepass)。
sentinel auth-pass mymaster ibethfy
#配置多少毫秒后没收到主节点的反馈,则主观认为主节点down了。
sentinel down-after-milliseconds mymaster 5000
#failover过期时间。当failover开始后,在此时间内仍然没有触发任何failover操作,
#当前sentinel将会认为此次failover失败。
sentinel failover-timeout mymaster 30000
#切换到了新的节点后,slaves对新master进行复制
sentinel parallel-syncs mymaster 1
5. 精简配置文件(去除注释和空行)
cat sentinel.conf | grep -v "#" | grep -v "^$" > redis-sentinel-26379.conf
cat redis-sentinel-26379.conf
----------------------------------------------------------------
port 26379
daemonize yes
pidfile /var/run/redis-sentinel-26379.pid
logfile "redis-sentinel-26379.log"
dir /usr/local/redis/data/
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
6. 根据redis-sentinel-26379.conf生成另外二个sentinel配置文件
#将文件中的文本中的”26379“文本字样全都替换成”26380“
sed "s/26379/26380/g" redis-sentinel-26379.conf > redis-sentinel-26380.conf
sed "s/26379/26381/g" redis-sentinel-26379.conf > redis-sentinel-26381.conf
注意:sentinel配置文件中只配置了主节点没有配置从节点,从节点信息是通过主节点的 info Replication获取到的:
redis-cli -h localhost -p 7000 info Replication
四、启动哨兵
1. 启动所有的哨兵
redis-sentinel redis-sentinel-26379.conf
redis-sentinel redis-sentinel-26380.conf
redis-sentinel redis-sentinel-26381.conf
2. 查看26379哨兵信息
guchunchao$ redis-cli -p 26379
127.0.0.1:26379> keys * ###很多Redis的命令不支持
(error) ERR unknown command `keys`, with args beginning with: `*`,
127.0.0.1:26379> ping ### 支持
PONG
127.0.0.1:26379> info ###支持
# Server
redis_version:5.0.4
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:aaa9fd100be76deb
redis_mode:sentinel
os:Darwin 17.7.0 x86_64
arch_bits:64
multiplexing_api:kqueue
atomicvar_api:atomic-builtin
gcc_version:4.2.1
process_id:2422
run_id:507272280f7e541f9d99a1b4bbea005a178b76f8
tcp_port:26379
uptime_in_seconds:70
uptime_in_days:0
hz:13
configured_hz:10
lru_clock:14672887
executable:/usr/local/redis/etc/sentinel/redis-sentinel
config_file:/usr/local/redis/etc/sentinel/mySentinel.conf
# Clients
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
# CPU
used_cpu_sys:0.208366
used_cpu_user:0.111376
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
# Stats
total_connections_received:1
total_commands_processed:1
instantaneous_ops_per_sec:0
total_net_input_bytes:66
total_net_output_bytes:129
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=1
127.0.0.1:26379>
3. 再次查看26379哨兵配置文件,发现配置文件有变化
guchunchaodeMacBook-Air:sentinel guchunchao$ cat mySentinel.conf
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
logfile "redis-sentinel-26379.log"
dir "/usr/local/redis/data"
sentinel myid b4cc05b4073f4865c28b8a815d2e36b68d4a05b9
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
# Generated by CONFIG REWRITE
protected-mode no
sentinel known-replica mymaster 127.0.0.1 7002
sentinel known-replica mymaster 127.0.0.1 7001
sentinel current-epoch 0
4. 按哨兵的端口号分别查询各个哨兵的信息
xiaochao$ redis-cli -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
xiaochao$ redis-cli -p 26380 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
xiaochao$ redis-cli -p 26381 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
五、Java客户端连接
1.流程图
客户端实现基本原理step-1 客户端实现基本原理step-2 客户端实现基本原理step-3 客户端实现基本原理step-4故障的发现和转移都是由sentinel来做的,客户端订阅sentinel的频道,频道里有谁是master的变化,如果master变了,频道就会publish一个消息给客户端。
2. 客户端接入流程
1) Sentinel地址集合
2) masterName
3) 不是代理模式(并不是通过sentinel代理间接地连接master,是直连。sentinel只是在管理master)
3. Jedis实现
public class TestSentinel {
static Logger logger = LoggerFactory.getLogger(TestSentinel.class);
public static void main(String[] args) {
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add("127.0.0.1:26379");
sentinels.add("127.0.0.1:26380");
sentinels.add("127.0.0.1:26381");
JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinels);
Jedis jedis = null;
while(true) {
try {
jedis = sentinelPool.getResource(); //从池中获取Redis连接
int index = new Random().nextInt(100000);
String key = "k-" + index;
String value = "v" + index;
jedis.set(key, value);
logger.info("{} value is {}",key,jedis.get(key));
TimeUnit.SECONDS.sleep(2);
} catch(Exception e) {
//当Redis的master被kill掉,这里会一直报错,时间为:
//sentinel down-after-milliseconds mymaster 5000 指定的时间
logger.error(e.getMessage(),e);
} finally {
if(jedis != null)
jedis.close(); //并不是真正的关闭,而是放回池子里
}
}
}
}
虽然名称是JedisSentinelPool,但并不是有个sentinelPool,每次都从SentinelPool中取。这个名称仅仅是为了区分JedisPool而已,JedisSentinelPool本质上还是客户端取的master。
端口号没配对,报如下错误
All sentinels down, cannot determine where is mymaster master is running...
4. 模拟master挂掉,查看Jedis客户端的状态
- ps aux | grep redis
- kill -9 master_pid
- 程序报异常 大约down-after-milliseconds指定的时间
- 然后程序可以重新正常运行
- 查看配置文件:
1)新的master配置文件中没有了:replicaof 127.0.0.1 7000
2)所有sentinel配置文件中 master的IP地址和端口号被修改
3)挂掉的主节点依然会
redis-cli -p newMasterIP info sentinel
发现slave节点依然是2,说明挂掉的主节点依然被sentinel监控着,一旦重启及被重新拉入集群中作为一个slave
六、实现原理
1. 三个定时任务
1) 每10秒每个sentinel对master和slave执行info操作。目的:
(1)发现slave
(2)确认主从关系
2) 每2秒每个sentinel通过master节点的channel交换信息(pub / sub)
(1)通过_sentinel_:hello频道交互
(2)交互对节点的看法和自身信息
3) 每一秒每个sentinel会对其他sentinel和Redis执行ping
2. 主观下线和客观下线
# quorum 英[ˈkwɔːrəm] 法定人数
#写的是master,其实也是对slave的一个要求
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
#sentinel每一秒对master和slave 进行ping,如果超过30秒还没收到回复,就主观判断其下线
主观下线:每个sentinel节点对Redis节点失败的“偏见”
由于网络分区等其他原因,可能当前的sentinel和Redis节点是不通的,或者网络是有问题的,但是你不能根据这个就断定Redis节点down掉了,这是一种“偏见”。怎么样做到公平呢?即通过客观下线:quorum个sentinel节点对Redis节点失败达成共识。通过第二个定时任务来达成共识的。
客观下线:所有sentinel节点对Redis节点失败“达成共识”(超过quorum个统一)
sentinel之间发送sentinel is-master-down-by-addr 询问其他sentinel关于Redis节点是否down掉,是否需要做客观下线。对于slave主观下线就行,没必要做客观下线,因为它不需要做故障转移。当同意的sentinel个数达到了quorum指定的法定人数时,即可做客观下线。
3. leader选举
- 原因:只有一个sentinel节点完成故障转移
- 选举:通过 sentinel is-master-down-by-addr命令希望成为leader
1. 每个主观下线的sentinel节点向其他sentinel节点发送命令,要求将它设置为leader。
2. 收到命令的sentinel节点如果没有同意过其他sentinel节点发送的命令,那么将同意该请求,否则拒绝。
3. 如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,那么它将成为leader。
4. 如果此过程中有多个sentinel节点成为了leader,那么将等待一段时间重新选举。
4. 故障转移
1. 从slave节点中选出一个“合适的”节点作为master节点
2. 对上面的节点执行slaveof no one 命令让其成为master节点
3. 向剩余slave节点发送命令,让他们成为新master节点的slave节点,
复制规则和parallel-syncs参数有关
----------------------------------------
例如:1主3从,主挂了,选出一个slave做master,还有2个slave,如果parallel-synchrs=2,
则2个从同时做复制操作。其实在master端是做了优化的,只做一次rdb的生成。
但是会同时去完成2个slave的rdb文件发送,有网络开销【要结合实际当中的master的内存大小来设置】)。
----------------------------------------
1) 资源合理使用和保护:parallel-synchrs=1,那么就是顺序执行,逐个slave进行copy。
2)如果读写分离,你想快速恢:parallel-synchrs的值就设置大一点。
4. 更新原来的master节点配置为slave,并通过定时任务对其保持“关注”,
当其恢复后命令它去复制新的master节点。
5. 选择“合适的”slave节点作为新的master节点
- 选择slave-priority 最高的节点,如果存在则返回,不存在则继续。(一般不配置;若某台机器的配置较高,可以将那台机器上的slave节点的优先级设高些。)
- 选择复制偏移量最大的slave节点(复制最完整),如果存在则返回,不存在则继续。
- 选择runId最小的slave节点,即:启动最早的节点。
六、常见开发运维问题
1. 节点运维(偏运维)
- 机器下线:例如过保等情况
- 机器性能不足:例如CPU、内存、硬盘、网络等
- 节点自身故障:例如服务不稳定等
1)节点下线
(1)主节点:手动故障转移,忽略了主观下线,客观下线,领导者选举
sentinel failover <mastername>
(2)从节点:临时下线还是永久下线?例如是否做一些清理工作。但是要考虑读写分离。
2)节点上线
(1)主节点:sentinel failover mastername进行上线
(2)从节点:slaveof 即可,sentinel节点可以感知
(3)sentinel节点:参考其它sentinel节点启动即可
2. 高可用读写分离(偏开发)
读写分离的问题:从节点下线的时候,无法完成客户端感知新节点。
七、本章回顾总结
- Redis Sentinel是Redis的高可用实现方案,故障发现、故障自动转移、配置中心、客户端通知。
- Sentinel是从Redis2.8版本才开始正式生产可用,之前版本生产不可用
- 尽可能在不同物理机上部署Redis节点
- Redis Sentinel中Sentinel节点数最好>=3且最好为奇数
- Redis Sentinel中Sentinel节点与普通节点没什么区别。
- 客户端初始化时连接到的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心,不是代理。第一次启用的时候拿master节点,而不是每一次都要询问它。
- Redis Sentinel通过三个定时任务实现了Sentinel节点对于主节点、从节点、其余sentinel节点的监控。
- Redis Sentinel在对节点做失败判定时分为主观下线和客观下线。
- 看懂Redis Sentinel故障转移日志对于Redis Sentinel以及问题排查非常有帮助。
- Redis Sentinel 实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis 数据节点的状态变化。(即:自己写代码拓展RedisSentinelPool中的)
三个消息:
+switch-master:切换为主节点(从节点晋升主节点)
+convert-to-slave:切换为从节点
+sdown:主观下线
网友评论