你好,我是悟空呀~
本文目錄如下:
一、前言
二、部署拓撲圖
三、搭建 Redis 一主兩從
1.1 備份和還原 Redis 鏡像
1.2 主節(jié)點配置
1.3 從節(jié)點配置
1.4 啟動容器
1.5 查看 Redis 狀態(tài)
1.6 測試主從復制
四、搭建哨兵集群
4.1 哨兵集群拓撲圖
4.2 哨兵服務是怎么啟動的
4.3 哨兵配置
4.4 啟動哨兵容器
五、客戶端自動感知故障
如何實現(xiàn)自動感知故障
客戶端自動感知的原理
六、遇到的問題
七、總結
一、前言
我們有個項目中用的 MySQL、Redis、ES、微服務都是單節(jié)點部署的,沒有做集群模式部署,為了提高整體的可用性,對項目的部署架構進行了升級,支持高可用。相關內容可參考之前的兩篇:
MySQL 高可用篇
ELK Stack 篇
通過本篇,你可以學到以下內容:
- Redis 真實的哨兵集群模式部署實戰(zhàn)。
- Redis 主節(jié)點宕機后,Redis 哨兵如何進行故障轉移的。
- Redis 切換主節(jié)點后,客戶端如何自動感知并連接新的主節(jié)點。
緩存實戰(zhàn)的文章寫了很多篇,把歷史文章匯總下,方便大家查看:
《緩存實戰(zhàn)(一)緩存初級打怪》
《緩存實戰(zhàn)(二)Redis分布式鎖》
《緩存實戰(zhàn)(三)Redisson 分布式鎖》
《緩存實戰(zhàn)(四)實戰(zhàn) Spring Cache》
《緩存實戰(zhàn)(五)Redis 開發(fā)手冊 | 花果山版》
《緩存實戰(zhàn)(六)詳解 Redis 冷備》
《緩存實戰(zhàn)(七)鏡 | 深入剖析主從架構原理》
《緩存實戰(zhàn)(八)「手摸手」主從環(huán)境的部署+壓測》
二、部署拓撲圖
我們項目的測試環(huán)境 12 臺服務器已經部署好了,其中三臺用來部署 Redis 的 一主 + 兩從 + 三哨兵的服務器。服務器資源清單如下:
另外 Redis 的端口都是 6379,Sentinel(哨兵) 的端口都是 26379。
部署的拓撲圖如下,三臺服務器上都部署了一個哨兵。
三、搭建 Redis 一主兩從
我們原來的單點的服務器上是有運行的 Redis 容器的,把這個容器的鏡像打包備份下,然后拷貝和還原到新的服務器上就好了。
搭建 Redis 一主兩從的步驟如下:
- 其中一臺服務器作為主節(jié)點,配置文件為主節(jié)點的,用 docker 啟動 Redis 主節(jié)點。
- 另外兩臺服務器作為從節(jié)點,配置文件為從節(jié)點的,用 docker 啟動兩個 Redis 從節(jié)點。
- 進入到主節(jié)點和從節(jié)點容器中,查看主從復制狀態(tài)。
1.1 備份和還原 Redis 鏡像
打包測試環(huán)境的 Redis 鏡像,這個命令會將服務器上 redis 鏡像打包成 tar 包,這樣我們就方便拷貝到其他服務器上了。執(zhí)行打包鏡像命令:
sudo docker save -o redis.tar redis:0.1
因為保存的 tar 包權限不夠,所以設置下權限為 777。執(zhí)行修改權限的命令:
sudo chmod 777 redis.tar
將這個 tar 包拷貝新環(huán)境的三臺服務器上。執(zhí)行導入鏡像的命令:
sudo docker load -i redis.tar
添加配置文件 redis.conf 放在本地,作為 redis 容器的配置文件。這個文件也可以在 redis 官網下載 https:///。redis.conf 文件放到 /home/redis 目錄下。
sudo mkdir /home/redis
1.2 主節(jié)點配置
修改本地的 redis.conf 文件:
requirepass abc123
masterauth abc123
requirepass 和 masterauth:對于數(shù)據(jù)比較重要的節(jié)點,主節(jié)點會通過設置requirepass參數(shù)進行密碼 驗證,這時所有的客戶端訪問必須使用auth命令實行校驗。從節(jié)點與主節(jié)點 的復制連接是通過一個特殊標識的客戶端來完成,因此需要配置從節(jié)點的 masterauth參數(shù)與主節(jié)點密碼保持一致,這樣從節(jié)點才可以正確地連接到主 節(jié)點并發(fā)起復制流程。
1.3 從節(jié)點配置
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 狀態(tài)
進入容器,連接 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 信息,代表當前節(jié)點作為主節(jié)點。
connected_slaves:0 代表連接的從節(jié)點為 0 個。
當三個節(jié)點的 redis 容器都啟動后,再次查看主節(jié)點的復制信息。發(fā)現(xiàn)有 connected_slaves:2 有兩個從節(jié)點連上了。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QPHj8Ohi-1654673955923)(../../images/image-20220608111236463.png)]
然后我們進入到從節(jié)點容器,查看從節(jié)點的復制信息。
發(fā)現(xiàn) role:slave,說明此節(jié)點作為從節(jié)點。
master_host:10.2.1.61,說明了主節(jié)點的 IP 是 10.2.1.61。

1.6 測試主從復制
我們可以用 Redis Desktop 工具測試主從數(shù)據(jù)是否一致。在主節(jié)點中寫入 abc=當前時間,發(fā)現(xiàn)另外兩個從節(jié)點也同步了這個數(shù)據(jù),說明主從復制成功。

四、搭建哨兵集群
4.1 哨兵集群拓撲圖
三臺機器上分別部署三個哨兵服務。部署拓撲圖如下所示:

4.2 哨兵服務是怎么啟動的
其實哨兵服務也是用 Redis 容器啟動的,只是用的不同命令的啟動的。
我們可以在 redis 容器中執(zhí)行如下命令啟動哨兵服務。
redis-sentinel /usr/local/etc/redis/sentinel.conf
因為我們是通過 docker 啟動的,所以在啟動 docker 容器時,帶上這個命令參數(shù)就可以啟動哨兵服務了,就不用到容器里面執(zhí)行這個命令了。
4.3 哨兵配置
首先創(chuàng)建兩個映射文件:sentinel.conf 和 sentinel-26379.log,第一個是配置哨兵參數(shù)的,第二個文件是哨兵的日志文件,這兩個文件都會在啟動 redis 容器時,映射到容器中,方便我們修改配置以及查看日志。
sentinel.conf 這個配置文件可以從官方的 redis 安裝包中拷貝,也可以自己創(chuàng)建一個配置文件,修改幾個參數(shù)就可以了??梢灾苯涌截愇业呐渲谩?/p>
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
# 監(jiān)控 redis 的 IP 和端口,這里監(jiān)控的 redis 的主節(jié)點
sentinel monitor mymaster 10.2.1.61 6379 2
# down-after-milliseconds:每個 Sentinel節(jié)點都要通過定期發(fā)送 ping 命令來判斷Redis數(shù)據(jù)節(jié)點和其 余Sentinel節(jié)點是否可達,如果超過了down-after-milliseconds配置的時間且沒 有有效的回復,則判定節(jié)點不可達,<times>(單位為毫秒)就是超時時 間。這個配置是對節(jié)點失敗判定的重要依據(jù)。對 Sentinel節(jié)點、主節(jié)點、從節(jié)點的失敗判定同時有效。
sentinel down-after-milliseconds mymaster 30000
# parallel-syncs 就是用來限制在一次故障轉移之后,每次向新的主節(jié) 點發(fā)起復制操作的從節(jié)點個數(shù)。
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:日志文件,哨兵監(jiān)控和故障轉移的日志都會存到這個日志里面。
sentinel monitor mymaster:監(jiān)控 redis 的 IP 和端口,這里監(jiān)控的 redis 的主節(jié)點。
down-after-milliseconds:每個 Sentinel節(jié)點都要通過定期發(fā)送 ping 命令來判斷Redis數(shù)據(jù)節(jié)點和其 余Sentinel節(jié)點是否可達,如果超過了down-after-milliseconds配置的時間且沒 有有效的回復,則判定節(jié)點不可達,(單位為毫秒)就是超時時間。這個配置是對節(jié)點失敗判定的重要依據(jù)。對 Sentinel節(jié)點、主節(jié)點、從節(jié)點的失敗判定同時有效。
parallel-syncs:就是用來限制在一次故障轉移之后,每次向新的主節(jié) 點發(fā)起復制操作的從節(jié)點個數(shù)。
failover-timeout:故障轉移超時時間。
auth-pass:當 redis 主從復制配置了密碼的話,則需要在這里配置密碼。
首先在本地創(chuàng)建日志文件用來存放哨兵日志。
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
參數(shù)替換,也就是使用最新的 redis 鏡像來啟動 sentinel。
查看啟動的容器,第一個是哨兵容器,名字是 mysentinel1
, 第二個是 redis 容器,名字是 redis
。如下圖所示:

當在三臺服務器上分別啟動 Redis 哨兵后,查看哨兵日志文件 sentinel-26379.log。

打印出了 Redis 的當前版本(5.0.14),運行模式(哨兵模式),端口號 26379,哨兵 id,監(jiān)控的 Redis 中的主節(jié)點,兩個 Redis 從節(jié)點和一個主節(jié)點的 IP 和 端口。
而且當我們去查看 Sentinel 配置文件時,發(fā)現(xiàn)配置文件中的內容發(fā)生了變化,在文件末尾追加了以下內容,含義就是 Sentinel 自動發(fā)現(xiàn)了其他 Redis 從節(jié)點
4.4.2 啟動第二個容器
執(zhí)行以下命令啟動第二個容器:
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 啟動第三個容器
執(zhí)行以下命令啟動第三個容器:
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 主從切換
停掉主節(jié)點的 redis 容器后,進入到第二個哨兵容器中,執(zhí)行以下命令查看哨兵的狀態(tài):
redis-cli -h 127.0.0.1 -p 26379 info Sentinel
發(fā)現(xiàn)主節(jié)點的 IP 已經從 10.2.1.61 切換為從節(jié)點的 IP(10.2.1.63)了。

進入到第二個 redis 容器中,查看同步狀態(tài)。主節(jié)點的 IP 也是 10.2.1.63
查看第三個哨兵的日志,可以看到哨兵通過以下步驟進行了主從切換。
重要的步驟說明如下:
- ① 主觀宕機,
+sdown
表示當前哨兵認為這個 Redis 節(jié)點(10.2.1.61)宕機了。 - ② 客觀宕機,
+odown
表示有幾個哨兵認為這個 Redis 節(jié)點(10.2.1.61)宕機了??吹降?quorun 4/2,表示有 4 個哨兵認為 Redis 節(jié)點宕機了,大于設置的 2,所以這個 Redis 節(jié)點是真的宕機了。(這里為什么是 4,而不是哨兵數(shù) 3,筆者也沒有弄清楚,歡迎留言討論。) - ③ 投票選舉新的哨兵 Leader,三個哨兵都將票投給了 id = 2abxxx 的哨兵,也就是第三個哨兵節(jié)點,將由這個哨兵進行主從切換。
- ④ 開始故障轉移,failover-state-select-slave 表示要轉移哪個故障節(jié)點。這里就是切換主節(jié)點。
- ⑤ 選出主節(jié)點為第三個 Redis 節(jié)點(10.2.1.63)。
- ⑥ 切換主節(jié)點為第三個 Redis 節(jié)點(10.2.1.63)。
說明哨兵集群模式下對 Redis 的狀態(tài)監(jiān)控和主從切換成功。
五、客戶端自動感知故障
如何實現(xiàn)自動感知故障
我們項目中,都是用 Redis 客戶端去讀寫 Redis,在單機情況下,單節(jié)點 Redis 宕機后,客戶端肯定會報錯,我們可以嘗試恢復這個服務器上的 Redis 就好了。
但是我們現(xiàn)在是有多個 Redis 節(jié)點的,應用程序該如何配置呢?
可能的思路是這樣:應用程序配置主節(jié)點的 IP 地址和端口。缺點:主節(jié)點切換后,需要改配置并重啟應用。
那有沒有一種方案是自動感知到 Redis 宕機后,連接到新的主節(jié)點的呢?
有的,我們加下 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 主節(jié)點的容器。測試往 redis 寫入數(shù)據(jù),程序會報錯。
寫入數(shù)據(jù)的代碼:
stringRedisTemplate.opsForValue()
.setIfAbsent(key, toJson(value),
millisecond, TimeUnit.MILLISECONDS);
報錯信息如下:
nested exception is redis.clients
.jedis.exceptions.JedisConnectionException:
Unexpected end of stream.
因為故障轉移是需要一定時間的,過幾秒后后發(fā)現(xiàn)控制臺窗口打印出 Redis 的主節(jié)點為 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,都是正常的,且往第三個節(jié)點寫入數(shù)據(jù)后,第二個節(jié)點也進行了主從復制。
客戶端自動感知的原理
我們項目中用的 Jedis 客戶端,它有一個連接池 JedisPool,當訪問 Redis 時,會從連接池里面獲取一個連接。我們看下這個連接池里面的信息。如下圖所示:

masterListeners 代表對三個 Redis 節(jié)點的監(jiān)聽器。里面指定了 Redis 節(jié)點的 IP 和 Port。
currentHostMaster 代表當前連接的主節(jié)點。目前為第三個節(jié)點。
當我們停掉 Redis 主節(jié)點后,哨兵會切換主節(jié)點,這個連接池里面的 currentHostMaster 也會被更新為新的主節(jié)點。當我們再次訪問 Redis 時,會和新的主節(jié)點建立連接。
六、遇到的問題
6.1 提示不能寫只讀的 redis 節(jié)點
READONLY You can't write against a read only replica.;
解決方案:每個哨兵都需要配置成監(jiān)控主節(jié)點 node1 的 IP。
6.2 提示連接 Redis 失敗
ERR Client sent AUTH, but no password is set
解決方案:主從節(jié)點都需要配置 requirepass 和 masterauth。
七、總結
本篇講解了在真實的多臺服務器上如何部署 Redis 主從架構、哨兵集群,以及驗證主從復制和故障轉移。
然后對項目中使用 Redis 的地方,加入了哨兵配置,使其可以自動感知主從切換后的 IP 變化,從而和新的 Redis 主節(jié)點進行連接。
Redis Cluster 集群模式并沒有涉及到,大家有沒有興趣呢~~
您可能錯過了以下內容:
《緩存實戰(zhàn)(一)緩存初級打怪》
《緩存實戰(zhàn)(二)Redis分布式鎖》
《緩存實戰(zhàn)(三)Redisson 分布式鎖》
《緩存實戰(zhàn)(四)實戰(zhàn) Spring Cache》
《緩存實戰(zhàn)(五)Redis 開發(fā)手冊 | 花果山版》
《緩存實戰(zhàn)(六)詳解 Redis 冷備》
《緩存實戰(zhàn)(七)鏡 | 深入剖析主從架構原理》
《緩存實戰(zhàn)(八)「手摸手」主從環(huán)境的部署+壓測》