Sentinel

在上节的 Redis 主从复制中,如果主节点发生故障,我们是希望从节点可以自动提升而不是人工来干预,Redis 提供的 Sentinel 功能就可实现此功能。

Sentinel简介

Redis Sentinel 是一个分布式系统,类似于 Consul 集群,一般由 3 ~ 5 个节点组成,使用 Raft 算法实现领导者选举因为故障转移工作只需要一个 Sentinel 节点来完成,如下图所示,我们客户端部分直接和 Sentinel 集群交互,关于 Redis 主从节点的状态维护交由 Sentinel 去管理。

Sentinel参数配置

port 6379 # 端口号
daemonize no # 是否为守护进程模式
pidfile /var/run/redis-sentinel.pid
logfile "" # 日志文件
dir /tmp # 工作目录

# 监控的 Master 名称,默认为 mymaster 可以设置不同的名称监控多个 Master,
sentinel monitor mymaster 127.0.0.1 6379 2

# Sentinel 通过 ping 命令检查 Master 是否可用,默认 30 秒
sentinel down-after-milliseconds mymaster 30000

# 限制每次向新的主节点发起复制操作的从节点数量,默认为 1
sentinel parallel-syncs mymaster 1

# 故障转移超时时间,默认 3 分钟,超过这个实践认为故障转移失败
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

Sentinel部署实践

在上节讲解的 Redis 主从复制一节,我们只用了一台机器,为了更好模拟生产环境,这里采用 3 台虚拟机进行实践,模拟故障转移。

以下为我们搭建好的示意图,客户端(Node.js 服务)通过链接 Sentinel 与各个 Redis 主从节点交互。

环境准备

Redis 安装目录 /data/soft/redis-5.0.5/

机器 模式 节点 配置文件
192.168.6.128 Master 6379 redis-6379.conf
192.168.6.129 Slave1 6380 redis-6380.conf
192.168.6.130 Slave2 6381 redis-6381.conf
192.168.6.128 Sentinel1 26379 sentinel-26379.conf
192.168.6.129 Sentinel2 26379 sentinel-26379.conf
192.168.6.130 Sentinel3 26379 sentinel-26379.conf
机器 客户端服务 端口
192.168.6.131 node-sentinel-demo 3000

配置Redis主从

在主从配置一节有讲解,当时是在一台虚拟机上完成的,现在采用多台虚拟机,区别不大,建议还是先看下主从复制一节助您更好理解本节内容

redis-6379.conf

$ cat redis.conf | grep -v "#" | grep -v "^$" > redis-6379.conf # 去掉了注释和空格
bind 192.168.6.128
port 6379
daemonize yes # 开启守护进程
pidfile /var/run/redis_6379.pid
logfile "6379.log"
# save 900 1
# save 300 10
# save 60 10000
dbfilename dump-6379.rdb
dir /data/soft/redis-5.0.5/data/
replica-read-only yes # 默认从节点仅是只读模式

启动6379

$ src/redis-server redis-6379.conf

redis-6380.conf

$ cat redis.conf | grep -v "#" | grep -v "^$" > redis-6380.conf # 去掉了注释和空格
# redis-6380.conf
# 修改以下内容
bind 192.168.6.129
port 6380
daemonize yes # 开启守护进程
pidfile /var/run/redis_6380.pid
logfile "6380.log"
# save 900 1
# save 300 10
# save 60 10000
dbfilename dump-6380.rdb
dir /data/soft/redis-5.0.5/data/
replicaof 192.168.6.128 6379 # 做为该地址端口的从节点

启动6380

$ src/redis-server redis-6380.conf

redis-6381.conf

$ cat redis.conf | grep -v "#" | grep -v "^$" > redis-6381.conf # 去掉了注释和空格
# redis-6381.conf
# 修改以下内容
bind 192.168.6.130
port 6381
daemonize yes # 开启守护进程
pidfile /var/run/redis_6381.pid
logfile "6381.log"
# save 900 1
# save 300 10
# save 60 10000
dbfilename dump-6381.rdb
dir /data/soft/redis-5.0.5/data/
replicaof 192.168.6.128 6379 # 做为该地址端口的从节点

启动6381

$ src/redis-server redis-6381.conf

检查主从是否启动成功

$ src/redis-cli -h 192.168.6.128 -p 6379 client list
id=3 addr=192.168.6.129:46818 fd=7 name= age=229 idle=0 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
id=6 addr=192.168.6.130:39904 fd=8 name= age=91 idle=1 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
id=8 addr=192.168.6.128:39582 fd=9 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
$ redis-cli -h 192.168.6.128 -p 6379 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.6.129,port=6380,state=online,offset=364,lag=1
slave1:ip=192.168.6.130,port=6381,state=online,offset=364,lag=0
master_replid:f42c329990f416d2e7f62704bfa07ba231275cb0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:364
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:364

配置Sentinel集群

Sentinel 集群建议至少 3 个节点且总数最好为奇数,保证了高可用和公平性。由于部署在不同虚拟机上所以端口都为 26379 这里我暂时没有改动,如果是同一台机器部署 则需要更改端口号。

修改 Redis-Sentinel 配置

在 192.168.6.128、192.168.6.129、192.168.6.130 三台机器上分别进行如下操作:

$ cat sentinel.conf | grep -v "#" | grep -v "^$" > sentinel-26379.conf
$ vim

port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile "26379.log"
dir /data/soft/redis-5.0.5/data/
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

启动 Redis-Sentinel

如下所示两种方式启动 Redis-Sentinel,需要在 Sentinel 对应的多台机器上执行以下两种方式的其中一种进行操作

$ src/redis-sentinel sentinel-26379.conf # 方法一
$ src/redis-server sentinel-26379.conf --sentinel # 方法二

检查 Redis-Sentinel 集群状态

如下所示三个 Sentinel 节点已成功开启

$ src/redis-cli -h 192.168.6.128 -p 26379 client list
id=3 addr=192.168.6.130:46518 fd=16 name=sentinel-9929680e-cmd age=105 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=publish
id=4 addr=192.168.6.129:51008 fd=17 name=sentinel-cf0d2de4-cmd age=105 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=6 addr=192.168.6.128:44044 fd=18 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client

如下所示,展示了目前 Sentinel 所监控的主节点(192.168.6.128:6379)且该主节点有 2 个 Slave 节点还有 sentinels 节点数为 3

$ src/redis-cli -h 192.168.6.129 -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=192.168.6.128:6379,slaves=2,sentinels=3

至此 Sentinel 的服务端安装部署已经介绍完毕,接下来使用 Node.js 客户进行链接和故障转移演练实践

Nodejs客户端链接

在看下客户端如何使用 Sentinel,这里以 Node.js 为例,客户端通过 Sentinel 发现主从节点地址,然后在通过这些地址建立相应的链接来进行数据存取操作,以下用的是 Node.js 的 ioredis 客户端,你需要先执行 npm install ioredis 命令进行安装

const Redis = require('ioredis');
const redis = new Redis({
    sentinels:[
        { host: '192.168.6.128', port: 26379 },
        { host: '192.168.6.129', port: 26379 },
        { host: '192.168.6.130', port: 26379 },
    ],
    name: 'mymaster',
});

let count = 0;
setInterval(async function() {
    count++;
    const key = `k_${count}`;

    try {
        await redis.set(key, count);
        console.log(key, redis.get(key));
    } catch (err) {
        console.error(err);
    }
}, 1000)

故障转移演练

主节点宕机

192.168.6.128:6379 为我们最初设定的主节点,现在我们让主节点宕机来进行一次故障转移实战演练

$  ps -ef | grep redis-server

root       5080      1  0 20:04 ?        00:00:49 src/redis-server 192.168.6.128:6379

上面的 5080 为主节点的进程 id,执行 kill 命令使其断掉链接。

kill -9 5080

观察客户端日志

现在让我们在来看下上面写的 Node.js 客户端日志情况,如下所示:

k_1 1
k_2 2
k_3 3
[ioredis] Unhandled error event: Error: connect ECONNREFUSED 192.168.6.128:6379
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1198:14)
[ioredis] Unhandled error event: Error: connect ECONNREFUSED 192.168.6.128:6379
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1198:14)
... # 大概30秒之后恢复正常

观察服务端从节点日志

查看 6381 节点日志信息 tail -100 6381.log,在 33:01 秒链接被拒,在 33:31.546 时间点收到一条 user 请求该节点成为了 Master 节点,23:33:32.064 时间点向 6380 发送了一条请求使其成为从节点。

20945:S 29 Jun 2019 21:24:37.848 * Successful partial resynchronization with master.
20945:S 29 Jun 2019 21:24:37.848 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
20945:S 29 Jun 2019 23:33:01.104 # Connection with master lost.
20945:S 29 Jun 2019 23:33:01.107 * Caching the disconnected master state.
20945:S 29 Jun 2019 23:33:01.524 * Connecting to MASTER 192.168.6.128:6379
20945:S 29 Jun 2019 23:33:01.527 * MASTER <-> REPLICA sync started
20945:S 29 Jun 2019 23:33:01.527 # Error condition on socket for SYNC: Connection refused
... 中间省略,一直在重连
20945:S 29 Jun 2019 23:33:30.884 * Connecting to MASTER 192.168.6.128:6379
20945:S 29 Jun 2019 23:33:30.885 * MASTER <-> REPLICA sync started
20945:S 29 Jun 2019 23:33:30.885 # Error condition on socket for SYNC: Connection refused
20945:M 29 Jun 2019 23:33:31.541 # Setting secondary replication ID to f42c329990f416d2e7f62704bfa07ba231275cb0, valid up to offset: 2106538. New replication ID is b456d532b1f700037e141d56cf294668d12634e3
20945:M 29 Jun 2019 23:33:31.541 * Discarding previously cached master state.
20945:M 29 Jun 2019 23:33:31.546 * MASTER MODE enabled (user request from 'id=6 addr=192.168.6.129:40770 fd=10 name=sentinel-cf0d2de4-cmd age=11529 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=140 qbuf-free=32628 obl=36 oll=0 omem=0 events=r cmd=exec')
20945:M 29 Jun 2019 23:33:31.550 # CONFIG REWRITE executed with success.
20945:M 29 Jun 2019 23:33:32.064 * Replica 192.168.6.129:6380 asks for synchronization
20945:M 29 Jun 2019 23:33:32.064 * Partial resynchronization request from 192.168.6.129:6380 accepted. Sending 164 bytes of backlog starting from offset 2106538.

Redis 主从状态验证

让我们在最后做次验证,如下所示,192.168.6.130:6381 端口已成功晋升为 Master 节点,其从节点仅为1个 192.168.6.129:6380,因为 192.168.6.129:6379 端口已经被我们关闭了。

$ src/redis-cli -h 192.168.6.130 -p 6381 info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.6.129,port=6380,state=online,offset=2607904,lag=0
master_replid:b456d532b1f700037e141d56cf294668d12634e3
master_replid2:f42c329990f416d2e7f62704bfa07ba231275cb0
master_repl_offset:2607904
second_repl_offset:2106538
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1559329
repl_backlog_histlen:1048576

当前节点状态如下所示

如下图所示,主节点挂掉后,原先的主节点断开了与其从节点的复制,一个从节点被提升为新的主节点,其它从节点开始与新的主节点建立复制关系,在客户端这边是不受影响,它会通过 Sentinel 这个中间人进行处理不受影响。

恢复 192.168.6.128:6379 节点

执行命令 src/redis-server redis-6379.conf,恢复 6379 节点,Sentinel 也会持续的监控我们已经挂掉的 6379 节点,恢复之后集群调整如下所示

更多书籍推荐

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

results matching ""

    No results matching ""