知识点

  • 哨兵节点是一种特殊的redis节点
  • 哨兵节点和数据节点相互独立
  • 基于哨兵机制的高可用方案本质是带自动故障转移的主从复制
  • 哨兵集群监视数据节点工作状态,发现主节点掉线后对数据集群进行故障转移
  • 哨兵集群通过选举方式选出Leader节点

准备数据节点配置文件

master.conf

  1. # 绑定监听ip
  2. bind 192.168.124.25
  3. # 绑定监听端口
  4. port 9000
  5. # 指定log位置
  6. logfile "/data/log"
  7. # 打开AOF
  8. appendonly yes

slave-1.conf

  1. # 绑定监听ip
  2. bind 192.168.124.25
  3. # 绑定监听端口
  4. port 9001
  5. # 指定主节点
  6. slaveof 192.168.124.25 9000
  7. # 指定log位置
  8. logfile "/data/log"
  9. # 打开AOF
  10. appendonly yes

slave-2.conf

  1. # 绑定监听ip
  2. bind 192.168.124.25
  3. # 绑定监听端口
  4. port 9002
  5. # 指定主节点
  6. slaveof 192.168.124.25 9000
  7. # 指定log位置
  8. logfile "/data/log"
  9. # 打开AOF
  10. appendonly yes

准备哨兵节点配置文件

sentinel-1.conf.raw

  1. # 绑定监听端口
  2. port 9003
  3. # 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
  4. # 判断主节点主观掉线,集群才会认为主节点客观掉线
  5. sentinel monitor mymaster 192.168.124.25 9000 2
  6. # 节点持续5000ms没有响应判定节点不可达
  7. sentinel down-after-milliseconds mymaster 5000
  8. # 故障转移失败的判定时长
  9. sentinel failover-timeout mymaster 60000
  10. # 故障转移时同时发起复制操作的节点数
  11. sentinel parallel-syncs mymaster 1

sentinel-2.conf.raw

  1. # 绑定监听端口
  2. port 9004
  3. # 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
  4. # 判断主节点主观掉线,集群才会认为主节点客观掉线
  5. sentinel monitor mymaster 192.168.124.25 9000 2
  6. # 节点持续5000ms没有响应判定节点不可达
  7. sentinel down-after-milliseconds mymaster 5000
  8. # 故障转移失败的判定时长
  9. sentinel failover-timeout mymaster 60000
  10. # 故障转移时同时发起复制操作的节点数
  11. sentinel parallel-syncs mymaster 1

sentinel-3.conf.raw

  1. # 绑定监听端口
  2. port 9005
  3. # 指定监听主机的别名、ip和端口,需要有2个及以上哨兵节点
  4. # 判断主节点主观掉线,集群才会认为主节点客观掉线
  5. sentinel monitor mymaster 192.168.124.25 9000 2
  6. # 节点持续5000ms没有响应判定节点不可达
  7. sentinel down-after-milliseconds mymaster 5000
  8. # 故障转移失败的判定时长
  9. sentinel failover-timeout mymaster 60000
  10. # 故障转移时同时发起复制操作的节点数
  11. sentinel parallel-syncs mymaster 1

启动数据节点

  1. docker run -d --name redis-master-9000 --net=host -v $PWD/master-data:/data \
  2. -v $PWD/master.conf:/etc/redis.conf redis redis-server /etc/redis.conf
  3. docker run -d --name redis-slave1-9001 --net=host -v $PWD/slave1-data:/data \
  4. -v $PWD/slave-1.conf:/etc/redis.conf redis redis-server /etc/redis.conf
  5. docker run -d --name redis-slave2-9002 --net=host -v $PWD/slave2-data:/data \
  6. -v $PWD/slave-2.conf:/etc/redis.conf redis redis-server /etc/redis.conf

启动哨兵节点

  1. cp sentinel-1.conf.raw sentinel-1.conf
  2. cp sentinel-2.conf.raw sentinel-2.conf
  3. cp sentinel-3.conf.raw sentinel-3.conf
  4. docker run -d --name redis-sentinel1-9003 --net=host -v $PWD/sentinel1-data:/data \
  5. -v $PWD/sentinel-1.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf
  6. docker run -d --name redis-sentinel2-9004 --net=host -v $PWD/sentinel2-data:/data \
  7. -v $PWD/sentinel-2.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf
  8. docker run -d --name redis-sentinel3-9005 --net=host -v $PWD/sentinel3-data:/data \
  9. -v $PWD/sentinel-3.conf:/etc/redis.conf redis redis-sentinel /etc/redis.conf

停止主节点

  1. docker stop redis-master-9000

查看节点状态

  1. 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

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/go-redis/redis"
  5. "math/rand"
  6. "strconv"
  7. "testing"
  8. )
  9. var sentinelOptions = redis.FailoverOptions{
  10. MasterName: "mymaster",
  11. SentinelAddrs: []string{
  12. "192.168.124.25:9003",
  13. "192.168.124.25:9004",
  14. "192.168.124.25:9005",
  15. },
  16. }
  17. const SentinelRWKey = "sentinel-rw"
  18. func TestConnectSentinel(t *testing.T) {
  19. sentinelClient := redis.NewFailoverClient(&sentinelOptions)
  20. _, err := sentinelClient.Ping().Result()
  21. if err != nil {
  22. t.Fatal("哨兵连接失败。原因:", err)
  23. }
  24. fmt.Println("哨兵连接成功")
  25. }
  26. func TestSentinelRW(t *testing.T) {
  27. sentinelClient := redis.NewFailoverClient(&sentinelOptions)
  28. randomNum := rand.Int()
  29. _, err := sentinelClient.Set(SentinelRWKey, randomNum, 0).Result()
  30. if err != nil {
  31. t.Fatal("通过哨兵客户端写入数据失败。原因:", err)
  32. }
  33. resNum, err := sentinelClient.Get(SentinelRWKey).Result()
  34. if err != nil {
  35. t.Fatal("通过哨兵客户端读取数据失败。原因:", err)
  36. }
  37. if resNum != strconv.Itoa(randomNum) {
  38. t.Fatal("读出数据与写入数据不一致")
  39. }
  40. fmt.Println("哨兵读写成功")
  41. }