你好,我是悟空呀~
本文目录如下:
一、前言
二、部署拓扑图
三、搭建 Redis 一主两从
1.1 备份和还原 Redis 镜像
1.2 主节点配置
1.3 从节点配置
1.4 启动容器
1.5 查看 Redis 状态
1.6 测试主从复制
四、搭建哨兵集群
4.1 哨兵集群拓扑图
4.2 哨兵服务是怎么启动的
4.3 哨兵配置
4.4 启动哨兵容器
五、客户端自动感知故障
六、遇到的问题
6.1 提示不能写只读的 redis 节点
6.2 提示连接 Redis 失败
七、总结
一、前言
我们有个项目中用的 MySQL、Redis、ES、微服务都是单节点部署的,没有做集群模式部署,为了提高整体的可用性,对项目的部署架构进行了升级,支持高可用。相关内容可参考之前的两篇:
MySQL 高可用篇
ELK Stack 篇
通过本篇,你可以学到以下内容:
- Redis 主节点宕机后,Redis 哨兵如何进行故障转移的。
- Redis 切换主节点后,客户端如何自动感知并连接新的主节点。
缓存实战的文章写了很多篇,把历史文章汇总下,方便大家查看:
《缓存实战(一)缓存初级打怪》
《缓存实战(二)Redis分布式锁》
《缓存实战(三)Redisson 分布式锁》
《缓存实战(四)实战 Spring Cache》
《缓存实战(五)Redis 开发手册 | 花果山版》
《缓存实战(六)详解 Redis 冷备》
《缓存实战(七)镜 | 深入剖析主从架构原理》
《缓存实战(八)「手摸手」主从环境的部署+压测》
二、部署拓扑图
我们项目的测试环境 12 台服务器已经部署好了,其中三台用来部署 Redis 的 一主 + 两从 + 三哨兵的服务器。服务器资源清单如下:
另外 Redis 的端口都是 6379,Sentinel(哨兵) 的端口都是 26379。
部署的拓扑图如下,三台服务器上都部署了一个哨兵。
三、搭建 Redis 一主两从
我们原来的单点的服务器上是有运行的 Redis 容器的,把这个容器的镜像打包备份下,然后拷贝和还原到新的服务器上就好了。
搭建 Redis 一主两从的步骤如下:
- 其中一台服务器作为主节点,配置文件为主节点的,用 docker 启动 Redis 主节点。
- 另外两台服务器作为从节点,配置文件为从节点的,用 docker 启动两个 Redis 从节点。
1.1 备份和还原 Redis 镜像
打包测试环境的 Redis 镜像,这个命令会将服务器上 redis 镜像打包成 tar 包,这样我们就方便拷贝到其他服务器上了。执行打包镜像命令:
sudo docker save -o redis.tar redis:0.1
因为保存的 tar 包权限不够,所以设置下权限为 777。执行修改权限的命令:
sudo chmod 777 redis.tar
将这个 tar 包拷贝新环境的三台服务器上。执行导入镜像的命令:
sudo docker load -i redis.tar
添加配置文件 redis.conf 放在本地,作为 redis 容器的配置文件。这个文件也可以在 redis 官网下载 https:///。redis.conf 文件放到 /home/redis 目录下。
sudo mkdir /home/redis
1.2 主节点配置
修改本地的 redis.conf 文件:
requirepass abc123
masterauth abc123
requirepass 和 masterauth:对于数据比较重要的节点,主节点会通过设置requirepass参数进行密码 验证,这时所有的客户端访问必须使用auth命令实行校验。从节点与主节点 的复制连接是通过一个特殊标识的客户端来完成,因此需要配置从节点的 masterauth参数与主节点密码保持一致,这样从节点才可以正确地连接到主 节点并发起复制流程。
1.3 从节点配置
slave-read-only yes
requirepass abc123
masterauth abc123
slaveof 10.2.1.61 6379
1.4 启动容器
需要注意的是需要映射本地文件夹。
sudo docker run -p 6379:6379 --restart=always --name redis \
-v /home/redis/redis.conf:/usr/local/etc/redis/redis.conf \
-v /home/redisdata:/data/ \
-d 301
-v 代表映射的文件或文件夹,这里映射了 redis.conf 文件和 data 目录。data 目录会存放 Redis 的 AOF
和 RDB
持久化文件。
-d 表示后台运行,46b 代表镜像 id。因为我们服务器是没有外网的,所以用的是本地镜像启动的,如果你的服务器有外网,完全可以用官网的 redis 镜像启动。
1.5 查看 Redis 状态
进入容器,连接 redis,node1的 redis 密码是 abc123
# 查询容器 id
docker ps
# 进入 mysql 容器
docker exec -it <容器 id> /bin/bash
# 连接 redis
redis-cli -h localhost -p 6379 -a abc123
# 查看复制信息
info replication
只启动 node1 时查看 redis 复制信息
可以看到 role:master 信息,代表当前节点作为主节点。
connected_slaves:0 代表连接的从节点为 0 个。
当三个节点的 redis 容器都启动后,再次查看主节点的复制信息。发现有 connected_slaves:2 有两个从节点连上了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QPHj8Ohi-1654673955923)(../../images/image-20220608111236463.png)]
然后我们进入到从节点容器,查看从节点的复制信息。
发现 role:slave,说明此节点作为从节点。
master_host:10.2.1.61,说明了主节点的 IP 是 10.2.1.61。
1.6 测试主从复制
我们可以用 Redis Desktop 工具测试主从数据是否一致。在主节点中写入 abc=当前时间,发现另外两个从节点也同步了这个数据,说明主从复制成功。
四、搭建哨兵集群
4.1 哨兵集群拓扑图
三台机器上分别部署三个哨兵服务。部署拓扑图如下所示:
4.2 哨兵服务是怎么启动的
其实哨兵服务也是用 Redis 容器启动的,只是用的不同命令的启动的。
我们可以在 redis 容器中执行如下命令启动哨兵服务。
redis-sentinel /usr/local/etc/redis/sentinel.conf
因为我们是通过 docker 启动的,所以在启动 docker 容器时,带上这个命令参数就可以启动哨兵服务了,就不用到容器里面执行这个命令了。
4.3 哨兵配置
首先创建两个映射文件:sentinel.conf 和 sentinel-26379.log,第一个是配置哨兵参数的,第二个文件是哨兵的日志文件,这两个文件都会在启动 redis 容器时,映射到容器中,方便我们修改配置以及查看日志。
sentinel.conf 这个配置文件可以从官方的 redis 安装包中拷贝,也可以自己创建一个配置文件,修改几个参数就可以了。可以直接拷贝我的配置。
mkdir /home/redis
vim /home/redis/sentinel.conf
配置内容如下:
# sentile 端口
port 26379
# 是否在后台启动,默认为 no,注意这里需要设置为 no,否则容器启动不起来。
daemonize no
pidfile /var/run/redis-sentinel.pid
# 日志文件
logfile /var/log/sentinel-26379.log
# 工作目录
dir /tmp
# 监控 redis 的 IP 和端口,这里监控的 redis 的主节点
sentinel monitor mymaster 10.2.1.61 6379 2
# down-after-milliseconds:每个 Sentinel节点都要通过定期发送 ping 命令来判断Redis数据节点和其 余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没 有有效的回复,则判定节点不可达,<times>(单位为毫秒)就是超时时 间。这个配置是对节点失败判定的重要依据。对 Sentinel节点、主节点、从节点的失败判定同时有效。
sentinel down-after-milliseconds mymaster 30000
# parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节 点发起复制操作的从节点个数。
sentinel parallel-syncs mymaster 1
# 故障转移超时时间
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
# 当 redis 主从复制配置了密码的话,则需要在这里配置密码
sentinel auth-pass mymaster WHredis2020!
daemonize:是否在后台启动,默认为 no,注意这里需要设置为 no,否则容器启动不起来。
logfile:日志文件,哨兵监控和故障转移的日志都会存到这个日志里面。
sentinel monitor mymaster:监控 redis 的 IP 和端口,这里监控的 redis 的主节点。
down-after-milliseconds:每个 Sentinel节点都要通过定期发送 ping 命令来判断Redis数据节点和其 余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没 有有效的回复,则判定节点不可达,(单位为毫秒)就是超时时间。这个配置是对节点失败判定的重要依据。对 Sentinel节点、主节点、从节点的失败判定同时有效。
parallel-syncs:就是用来限制在一次故障转移之后,每次向新的主节 点发起复制操作的从节点个数。
failover-timeout:故障转移超时时间。
auth-pass:当 redis 主从复制配置了密码的话,则需要在这里配置密码。
首先在本地创建日志文件用来存放哨兵日志。
mkdir /home/redis/sentinel
vim /home/redis/sentinel/sentinel-26379.log
4.4 启动哨兵容器
4.4.1 启动一个 sentinel 容器
docker run --name mysentinel1 --restart=always -p 26379:26379 \
-v /home/redis/sentinel.conf:/usr/local/etc/redis/sentinel.conf \
-v /home/redis/sentinel/sentinel-26379.log:/var/log/sentinel-26379.log \
-d 9a2f \
redis-sentinel /usr/local/etc/redis/sentinel.conf
- --name mysentinel1,指定哨兵容器的名称为 mysentinel1。
- --restart=always,重启机器后,自动重启容器。
这里的 -d 9a2f 是 本地的 redis 容器 id,因为本机没有网络,所以这个镜像是从其他有网络的机器加载进来的。如果你们的机器有网络,完全可以用 -d redis
参数替换,也就是使用最新的 redis 镜像来启动 sentinel。
查看启动的容器,第一个是哨兵容器,名字是 mysentinel1
, 第二个是 redis 容器,名字是 redis
。如下图所示:
当在三台服务器上分别启动 Redis 哨兵后,查看哨兵日志文件 sentinel-26379.log。
打印出了 Redis 的当前版本(5.0.14),运行模式(哨兵模式),端口号 26379,哨兵 id,监控的 Redis 中的主节点,两个 Redis 从节点和一个主节点的 IP 和 端口。
而且当我们去查看 Sentinel 配置文件时,发现配置文件中的内容发生了变化,在文件末尾追加了以下内容,含义就是 Sentinel 自动发现了其他 Redis 从节点
4.4.2 启动第二个容器
执行以下命令启动第二个容器:
docker run --name mysentinel2 --restart=always -p 26379:26379 \
-v /home/redis/sentinel.conf:/usr/local/etc/redis/sentinel.conf \
-v /home/redis/sentinel/sentinel-26379.log:/var/log/sentinel-26379.log \
-d 9a2f \
redis-sentinel /usr/local/etc/redis/sentinel.conf
4.4.3 启动第三个容器
执行以下命令启动第三个容器:
docker run --name mysentinel3 --restart=always -p 26379:26379 \
-v /home/redis/sentinel.conf:/usr/local/etc/redis/sentinel.conf \
-v /home/redis/sentinel/sentinel-26379.log:/var/log/sentinel-26379.log \
-d 9a2f \
redis-sentinel /usr/local/etc/redis/sentinel.conf
4.4.4 验证 Redis 主从切换
停掉主节点的 redis 容器后,进入到第二个哨兵容器中,执行以下命令查看哨兵的状态:
redis-cli -h 127.0.0.1 -p 26379 info Sentinel
发现主节点的 IP 已经从 10.2.1.61 切换为从节点的 IP(10.2.1.63)了。
进入到第二个 redis 容器中,查看同步状态。主节点的 IP 也是 10.2.1.63
查看第三个哨兵的日志,可以看到哨兵通过以下步骤进行了主从切换。
重要的步骤说明如下:
- ① 主观宕机,
+sdown
表示当前哨兵认为这个 Redis 节点(10.2.1.61)宕机了。 - ② 客观宕机,
+odown
表示有几个哨兵认为这个 Redis 节点(10.2.1.61)宕机了。看到的 quorun 4/2,表示有 4 个哨兵认为 Redis 节点宕机了,大于设置的 2,所以这个 Redis 节点是真的宕机了。(这里为什么是 4,而不是哨兵数 3,笔者也没有弄清楚,欢迎留言讨论。) - ③ 投票选举新的哨兵 Leader,三个哨兵都将票投给了 id = 2abxxx 的哨兵,也就是第三个哨兵节点,将由这个哨兵进行主从切换。
- ④ 开始故障转移,failover-state-select-slave 表示要转移哪个故障节点。这里就是切换主节点。
- ⑤ 选出主节点为第三个 Redis 节点(10.2.1.63)。
- ⑥ 切换主节点为第三个 Redis 节点(10.2.1.63)。
说明哨兵集群模式下对 Redis 的状态监控和主从切换成功。
五、客户端自动感知故障
如何实现自动感知故障
我们项目中,都是用 Redis 客户端去读写 Redis,在单机情况下,单节点 Redis 宕机后,客户端肯定会报错,我们可以尝试恢复这个服务器上的 Redis 就好了。
但是我们现在是有多个 Redis 节点的,应用程序该如何配置呢?
可能的思路是这样:应用程序配置主节点的 IP 地址和端口。缺点:主节点切换后,需要改配置并重启应用。
那有没有一种方案是自动感知到 Redis 宕机后,连接到新的主节点的呢?
有的,我们加下 Redis 的哨兵配置就可以了。配置内容如下所示:
spring:
redis:
database: 0
password: abc123 # 密码(默认为空)
timeout: 10000 # 连接超时时长(毫秒)
sentinel: #哨兵模式
master: mymaster #主服务器所在集群名称
nodes: 10.2.1.63:26379,10.2.1.62:26379,10.2.1.61:26379
配置好了后,重启应用程序,然后停掉 Redis 主节点的容器。测试往 redis 写入数据,程序会报错。
写入数据的代码:
stringRedisTemplate.opsForValue()
.setIfAbsent(key, toJson(value),
millisecond, TimeUnit.MILLISECONDS);
报错信息如下:
nested exception is redis.clients
.jedis.exceptions.JedisConnectionException:
Unexpected end of stream.
因为故障转移是需要一定时间的,过几秒后后发现控制台窗口打印出 Redis 的主节点为 10.2.1.63:6379,说明故障转移成功了。
[MasterListener-mymaster-[10.2.1.61:26379]]
INFO redis.clients.jedis.JedisSentinelPool
-Created JedisPool to master at
10.2.1.63:6379
再次测试读写 Redis,都是正常的,且往第三个节点写入数据后,第二个节点也进行了主从复制。
客户端自动感知的原理
我们项目中用的 Jedis 客户端,它有一个连接池 JedisPool,当访问 Redis 时,会从连接池里面获取一个连接。我们看下这个连接池里面的信息。如下图所示:
masterListeners 代表对三个 Redis 节点的监听器。里面指定了 Redis 节点的 IP 和 Port。
currentHostMaster 代表当前连接的主节点。目前为第三个节点。
当我们停掉 Redis 主节点后,哨兵会切换主节点,这个连接池里面的 currentHostMaster 也会被更新为新的主节点。当我们再次访问 Redis 时,会和新的主节点建立连接。
六、遇到的问题
6.1 提示不能写只读的 redis 节点
READONLY You can't write against a read only replica.;
解决方案:每个哨兵都需要配置成监控主节点 node1 的 IP。
6.2 提示连接 Redis 失败
ERR Client sent AUTH, but no password is set
解决方案:主从节点都需要配置 requirepass 和 masterauth。
七、总结
本篇讲解了在真实的多台服务器上如何部署 Redis 主从架构、哨兵集群,以及验证主从复制和故障转移。
然后对项目中使用 Redis 的地方,加入了哨兵配置,使其可以自动感知主从切换后的 IP 变化,从而和新的 Redis 主节点进行连接。
Redis Cluster 集群模式并没有涉及到,大家有没有兴趣呢~~
您可能错过了以下内容:
《缓存实战(一)缓存初级打怪》
《缓存实战(二)Redis分布式锁》
《缓存实战(三)Redisson 分布式锁》
《缓存实战(四)实战 Spring Cache》
《缓存实战(五)Redis 开发手册 | 花果山版》
《缓存实战(六)详解 Redis 冷备》
《缓存实战(七)镜 | 深入剖析主从架构原理》
《缓存实战(八)「手摸手」主从环境的部署+压测》