知识点
- 哨兵节点是一种特殊的redis节点
- 哨兵节点和数据节点相互独立
- 基于哨兵机制的高可用方案本质是带自动故障转移的主从复制
- 哨兵集群监视数据节点工作状态,发现主节点掉线后对数据集群进行故障转移
- 哨兵集群通过选举方式选出Leader节点
准备数据节点配置文件
master.conf
# 绑定监听ip
bind 192.168.124.25
# 绑定监听端口
port 9000
# 指定log位置
logfile "/data/log"
# 打开AOF
appendonly yes
slave-1.conf
# 绑定监听ip
bind 192.168.124.25
# 绑定监听端口
port 9001
# 指定主节点
slaveof 192.168.124.25 9000
# 指定log位置
logfile "/data/log"
# 打开AOF
appendonly yes
slave-2.conf
# 绑定监听ip
bind 192.168.124.25
# 绑定监听端口
port 9002
# 指定主节点
slaveof 192.168.124.25 9000
# 指定log位置
logfile "/data/log"
# 打开AOF
appendonly yes
准备哨兵节点配置文件
sentinel-1.conf.raw
# 绑定监听端口
port 9003
# 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
# 判断主节点主观掉线,集群才会认为主节点客观掉线
sentinel monitor mymaster 192.168.124.25 9000 2
# 节点持续5000ms没有响应判定节点不可达
sentinel down-after-milliseconds mymaster 5000
# 故障转移失败的判定时长
sentinel failover-timeout mymaster 60000
# 故障转移时同时发起复制操作的节点数
sentinel parallel-syncs mymaster 1
sentinel-2.conf.raw
# 绑定监听端口
port 9004
# 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
# 判断主节点主观掉线,集群才会认为主节点客观掉线
sentinel monitor mymaster 192.168.124.25 9000 2
# 节点持续5000ms没有响应判定节点不可达
sentinel down-after-milliseconds mymaster 5000
# 故障转移失败的判定时长
sentinel failover-timeout mymaster 60000
# 故障转移时同时发起复制操作的节点数
sentinel parallel-syncs mymaster 1
sentinel-3.conf.raw
# 绑定监听端口
port 9005
# 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
# 判断主节点主观掉线,集群才会认为主节点客观掉线
sentinel monitor mymaster 192.168.124.25 9000 2
# 节点持续5000ms没有响应判定节点不可达
sentinel down-after-milliseconds mymaster 5000
# 故障转移失败的判定时长
sentinel failover-timeout mymaster 60000
# 故障转移时同时发起复制操作的节点数
sentinel parallel-syncs mymaster 1
启动数据节点
docker run -d --name redis-master-9000 --net=host -v $PWD/master-data:/data \
-v $PWD/master.conf:/etc/redis.conf redis redis-server /etc/redis.conf
docker run -d --name redis-slave1-9001 --net=host -v $PWD/slave1-data:/data \
-v $PWD/slave-1.conf:/etc/redis.conf redis redis-server /etc/redis.conf
docker run -d --name redis-slave2-9002 --net=host -v $PWD/slave2-data:/data \
-v $PWD/slave-2.conf:/etc/redis.conf redis redis-server /etc/redis.conf
启动哨兵节点
cp sentinel-1.conf.raw sentinel-1.conf
cp sentinel-2.conf.raw sentinel-2.conf
cp sentinel-3.conf.raw sentinel-3.conf
docker run -d --name redis-sentinel1-9003 --net=host -v $PWD/sentinel1-data:/data \
-v $PWD/sentinel-1.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf
docker run -d --name redis-sentinel2-9004 --net=host -v $PWD/sentinel2-data:/data \
-v $PWD/sentinel-2.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf
docker run -d --name redis-sentinel3-9005 --net=host -v $PWD/sentinel3-data:/data \
-v $PWD/sentinel-3.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf
停止主节点
docker stop redis-master-9000
查看节点状态
redis-cli -p 9003 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.124.25:9002,slaves=2,sentinels=3
可以看出主节点从 :9000
变为了 :9002
测试程序
main.go
package main
import (
"fmt"
"github.com/go-redis/redis"
"math/rand"
"strconv"
"testing"
)
var sentinelOptions = redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{
"192.168.124.25:9003",
"192.168.124.25:9004",
"192.168.124.25:9005",
},
}
const SentinelRWKey = "sentinel-rw"
func TestConnectSentinel(t *testing.T) {
sentinelClient := redis.NewFailoverClient(&sentinelOptions)
_, err := sentinelClient.Ping().Result()
if err != nil {
t.Fatal("哨兵连接失败。原因:", err)
}
fmt.Println("哨兵连接成功")
}
func TestSentinelRW(t *testing.T) {
sentinelClient := redis.NewFailoverClient(&sentinelOptions)
randomNum := rand.Int()
_, err := sentinelClient.Set(SentinelRWKey, randomNum, 0).Result()
if err != nil {
t.Fatal("通过哨兵客户端写入数据失败。原因:", err)
}
resNum, err := sentinelClient.Get(SentinelRWKey).Result()
if err != nil {
t.Fatal("通过哨兵客户端读取数据失败。原因:", err)
}
if resNum != strconv.Itoa(randomNum) {
t.Fatal("读出数据与写入数据不一致")
}
fmt.Println("哨兵读写成功")
}