1.1.1. 12.Redis 集群-sentinel


sentinel

之前只说到主从方案,最终一致性。那么现在有个问题,如果当主节点发生故障时,就必须人为来操作修改主节点等等,这样效率太低,Redis 官方提供了一个方案-sentinel(哨兵)。

哨兵

如图所示,sentinel 可以看做是集群高可用的心脏,负责监视主从节点的健康状态,当主节点发生故障时,会在从节点中选取最优节点作为主节点。

客户端连接集群时,会首先连接 sentinel ,sentinel 会查询主节点的地址,并告诉客户端,客户端再去连接主节点进行数据交互。

如下图,当主节点发生故障时,客户端会重新向 sentinel 要新主节点的地址。原来的主从连接将断开,客户端和原来的主节点也会断开。如此应用程序无需重启即可完成主从节点切换。

当主节点挂掉后,原来的主从复制也断开。客户端和原主节点的连接也断开了。从节点被提升为主节点后,其他从节点和新主节点建立复制关系。客户端会通过新的主节点继续进行交互。

主节点故障

sentinel 会持续监视已经挂掉的主节点,待其恢复后,会变成新的从节点,集群将会调整为下图。原来的主节点变成了从节点,与新的主节点建立复制关系。

新集群

消息丢失

Redis 的复制关系是异步的,当主节点挂掉的时候,可能还没有将所有消息同步给从节点,那么这部分消息可能丢失。如果延迟特别大,丢的消息也就会越多。

sentinel 无法保证消息完全不丢失,但是肯依尽可能的保证少丢失。有下面两个选项

min-slaves-to-write 1
min-slaves-max-lag 10

第一个参数表示,至少有一个从节点在进行正常复制,否则停止对外服务,丧失可用性。 那何为正常复制,何为异常复制呢?这个是由第二个参数控制,它单位是秒,表示 10s 没收到从节点反馈,就意味从节点同步不正常,要么网络分区,要么一直没反馈。

机制

sentinel 的作用
  1. master 节点的状态监测
  2. 如果 master 节点异常,会进行 master-slave 切换,选举一个 slave 作为新 master。将之前的 master 作为 slave。
  3. master-slave 切换后,会修改 master 和 slave 的 conf 配置。新 master 的 conf 中会多出 slaveof 的配置。sentinel.conf 的配置也会改变。
sentinel 的工作方式(每个实例都执行的定时任务)
  1. 每个 sentinel 每 1 秒都会对已知的 master,slave 发送 ping 命令来确认其状态。
  2. 如果有一个实例距离最后一次有效回复 ping 的时间超过了配置中的 own-after-milliseconds 的值,那么这个实例将会被 sentinel 标记为「主观下线」。
  3. 如果一个实例被标记为「主观下线」,那么监视这个 master 的所有 sentinel 都要以每 1 秒的频率确认 master 的确进入了「主观下线」状态。
  4. 当有足够多(配置的数量)的 sentinel 在指定时间范围内标记了 master 为「主观下线」,则 master 会被标记为「客观下线」。
  5. 一般情况下,每个 sentinel 都会每 10 秒对已知的 master、slave 发送 info 命令。
  6. 当 master 被 sentinel 标记为 「客观下线」时,则 sentinel 从每 10 秒改为每 1 秒对下线的 master 的所有 slave 进行发送 info 命令。
  7. 如果没有足够数量的 sentinel 同意 master 下线,那么 master 的 「客观下线」将会被接触,如果 master 重新向 sentinel 的 ping 命令进行回复,那么「主观下线」将会被接触

每个 sentinel 都有三个定时任务

  1. 每 10 秒对 master 和 slave 进行 info 命令。其目的为: a. 发现 master 节点。 b. 确认主从关系。
  2. 每 2 秒每个 sentinel 通过 master 节点的 channel 交换信息(pub/sub)。 master 节点上有一个发布订阅的频道(sentinel:hello)。sentinel 节点通过 sentinel:hello 频道进行信息交换(对节点的"看法"和自身的信息),达成共识。
  3. 每 1 秒对其他 sentinel 和 redis 节点进行 ping 操作来相互监控。其实这是一个心跳检测,是失败的判断依据。

主观下线(Subjectively Down, 简称 SDOWN):单个 sentinel 对节点做出的判断。

客观下线(Objectively Down, 简称 ODOWN):多个的 sentinel 对同一个节点做出 SDOWN 判断。

sentinel.conf 中有 2 个配置说明:

  1. sentinel monitor [masterName] [ip] [port] [quorum] a. masterName: 顾名思义,master 节点的名字或者别名。ip 和 port 就是 master 节点的 ip 和端口号。 b. quorum 就是「客观下线」的依据,表示至少有 quorum 个 sentinel 认为 master 故障,才会对这个 master 进行下线并且执行「故障转移」。
  2. sentinel down-after-milliseconds [masterName] [timeout] a. timeout 为毫秒值,表示如果 sentinel 在 timeout 毫秒后仍然没有连通 master 或者 slave,将会主观认为该节点下线。 b. 只有 master 需要「客观下线」。因为 slave 下线不需要「故障转移」。

1

选举领头 sentinel(领导者选举)

当 master 被判断为「客观下线」时,需要多个 sentinel 进行协商,选举一个领头来对这个节点进行故障转移操作。 其规则是:

  1. 所有的 sentinel 都有资格被选举为领头。
  2. 所有 sentinel 都有且只有一次机会将某一个 sentinel 选作领头。一旦选定不能修改。
  3. 设置 sentinel 领头是先到先得,一旦当前选定,以后要求设置 sentienl 领头将会被拒绝。
  4. 每个发现节点「客观下线」的 sentinel,都会要求其他 sentinel 将自己作为领头。
  5. 当一个 sentinel(源 sentinel)向另一个sentinel(目 sentinel)发送 is-master-down-by-addr ip port current_epoch runid 命令的时候,runid 参数是 sentinel 运行id,就表示源 sentinel 要求目标 sentinel 选举其为领头。
  6. 源 sentinel 会检查目标sentinel对其要求设置成领头的回复,如果回复的 leader_runid 和 leader_epoch 为源 sentinel,表示目标 sentinel 同意将源sentinel设置成领头。
  7. 如果某个 sentinel 被半数以上的 sentinel 设置为领头,则选举成功。
  8. 如果在限定时间内没有选出领头,则过一段时间再尝试。

选举领头的作用: 因为只能有一个 sentinel 节点去完成「故障转移」。

sentinel is-master-down-by-add 这个命令有两个作用:

  1. 确认下线判断。
  2. 领头选举。
主从切换方案

sentinel 主要负责三个任务:

  1. 监控(Monitoring),监控主从节点是否运行正常。
  2. 提醒(Notification),如果被监控的节点发生问题,可以通过 api 向管理员或者其他应用程序发送通知。
  3. 自动故障转移(Automatic failover),当 master 发生故障时,sentinel 需要进行「故障转移」,将其中一个 slave 作为新 master,旧 master 作为其 slave。当客户端试图连接失效的 master 时,sentinel 会返回新的 master 地址给客户端。

「故障转移」分为三个步骤:

  1. 从下线的 master 的 slave 中选取一个 slave 作为新 master。 领头sentinel从剩下的从列表中选择优先级高的,如果优先级一样,选择偏移量最大的(偏移量大说明复制的数据比较新),如果偏移量一样,选择运行 id 最小的从服务。
  2. 对下线的 master 的 slave 对新的 master 进行复制关系。
  3. 当下线的 master 设置为新 master 的 slave。

Sentinel 状态的持久化 Sentinel 的状态会被持久化在 Sentinel 配置文件里面。每当 Sentinel 接收到一个新的配置, 或者当领头 Sentinel 为主服务器创建一个新的配置时, 这个配置会与配置纪元一起被保存到磁盘里面。这意味着停止和重启 Sentinel 进程都是安全的。

配置

基于 homestead 环境,Ubuntu 18.04

主从配置
# 跳转到 redis 配置目录
cd /etc/redis
# 复制两个从节点配置
sudo cp redis.conf redis-6381.conf
sudo cp redis.conf redis-6382.conf

打开其中一个从节点配置文件,修改。

#修改
port 6381

#修改
pidfile /var/run/redis/redis-server-6381.pid
#修改
logfile /var/log/redis/redis-server-6381.log

# 增加一行
slaveof 127.0.0.1 6379

保存之后,启动两个从节点实例

sudo redis-server /etc/redis/redis-6381.conf

sudo redis-server /etc/redis/redis-6382.conf

然后通过 redis-cli 进入主节点,使用命令 info 查看。

127.0.0.1:6379> info
.
.
.
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=784,lag=1
slave1:ip=127.0.0.1,port=6382,state=online,offset=784,lag=1
master_replid:125d233d1f1ddf1f52300d8ca4720a979b01283f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:798
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:798

省略了一部分结果,我们可以看到,

role:master #本机是『主节点』 connected_slaves:2 #有2个『从节点』 slave0:ip=127.0.0.1,port=6381,state=online,offset=784,lag=1 slave1:ip=127.0.0.1,port=6382,state=online,offset=784,lag=1 "从"服务器iP地址和端口是 6381 和 6382

接下来我们试验下,首先在主节点设置一个 key。

127.0.0.1:6379> set name demo1
OK
127.0.0.1:6379> get name
"demo1"
# 切换为从节点
vagrant@homestead:/etc/redis$ redis-cli -p 6381

127.0.0.1:6381> get name
"demo1"

可以看到从节点已经有值,表示主从节点已经有复制关系。

然后尝试在从节点进行写操作

127.0.0.1:6381> set name lisi
(error) READONLY You can't write against a read only slave.

报错,说明从节点只能读取,这就是读写分离。

在 laravel 中配置主从

在 config/database.php 中修改、

<?php

return [
·
·
·
    'redis' => [
        'client' => env('REDIS_CLIENT', 'predis'),
        'cluster' => false,
        'default'=>[
            'tcp://127.0.0.1:6379',
            'tcp://127.0.0.1:6381',
            'tcp://127.0.0.1:6382',
        ],
    ],
]
sentinel

由于当前环境没有安装 redis-sentinel,所以先安装

sudo apt-get install redis-sentinel

安装完成后,/etc/redis 目录下才会有 sentinel.conf 文件,打开文件并修改

port 26379

pidfile "/var/run/redis-sentinel-26379.pid"

logfile "/var/log/redis/redis-sentinel-26379.log"

#主节点别名为mymaster,后面是ip和端口,2代表判断主节点失败至少需要2个sentinel节点同意
sentinel monitor mymaster 127.0.0.1 6379 2 

# 设置密码
# sentinel auth-pass mymaster 123456

#主节点故障30秒后启用新的主节点
sentinel down-after-milliseconds mymaster 30000 

#故障转移时最多可以有1个从节点同时对主节点进行数据同步,数字越大,用时越短,存在网络和 IO 开销
sentinel parallel-syncs mymaster 1 

#故障转移超时时间180s:a 如果转移超时失败,下次转移时时间为之前的2倍;b 从节点变主节点时,从节点执行 slaveof no one 命令一直失败的话,当时间超过180S时,则故障转移失败;c 从节点复制新主节点时间超过180S转移失败
sentinel failover-timeout mymaster 180000

然后复制两份 sentinel.conf

vagrant@homestead:/etc/redis$ sudo cp sentinel.conf sentinel-26381.conf
vagrant@homestead:/etc/redis$ sudo cp sentinel.conf sentinel-26382.conf

分别打开修改

pidfile "/var/run/sentinel/redis-sentinel-26381.pid"
logfile "/var/log/redis/redis-sentinel-26381.log"

port 26381

# myid 为当前 sentinel 的 id,随意设置一个就行,只要不重复,最开始我就是没修改这个,导致始终无法建立多个 sentinel
sentinel myid 8f2969662f6bcd3553661608c213a3d637ab9987

上面配置完成后,启动

vagrant@homestead:/etc/redis$ sudo redis-sentinel sentinel.conf
vagrant@homestead:/etc/redis$ sudo redis-sentinel sentinel-26381.conf
vagrant@homestead:/etc/redis$ sudo redis-sentinel sentinel-26382.conf

当启动后,查看日志发现 /var/log/redis/redis-sentinel.log

29329:X 25 Sep 17:15:33.445 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
29329:X 25 Sep 17:15:33.445 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=29329, just started
29329:X 25 Sep 17:15:33.445 # Configuration loaded
29330:X 25 Sep 17:15:33.448 * Increased maximum number of open files to 10032 (it was originally set to 1024).

# 启动模式为 sentinel, 端口为 26379
29330:X 25 Sep 17:15:33.450 * Running mode=sentinel, port=26379.

# 它的 id 为 8f2969662f6bcd3553661608c213a3d637cd2331
29330:X 25 Sep 17:15:33.451 # Sentinel ID is 8f2969662f6bcd3553661608c213a3d637cd2331

# redis 集群主节点为 端口号 6379,当主节点发生故障时,需要至少两个 quorum 投票确定新主节点
29330:X 25 Sep 17:15:33.451 # +monitor master mymaster 127.0.0.1 6379 quorum 2

# 发现了两个新从节点
29330:X 25 Sep 17:15:33.455 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
29330:X 25 Sep 17:15:33.464 * +slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6379

# 发现了两个新 sentinel
29330:X 25 Sep 17:15:47.382 * +sentinel sentinel 8f2969662f6bcd3553661608c213a3d637ab9987 127.0.0.1 26381 @ mymaster 127.0.0.1 6382
29330:X 25 Sep 17:15:49.633 * +sentinel sentinel 8f2969662f6efg3553661608c213a3d637cd0000 127.0.0.1 26382 @ mymaster 127.0.0.1 6382

这个时候再打开 sentinel.conf 会发现多了几行,表示已知的从节点和 sentinel。

sentinel known-slave mymaster 127.0.0.1 6381
sentinel known-slave mymaster 127.0.0.1 6382
sentinel known-sentinel mymaster 127.0.0.1 26381 8f2969662f6bcd3553661608c213a3d637ab9987
sentinel known-sentinel mymaster 127.0.0.1 26382 8f2969662f6efg3553661608c213a3d637cd0000
sentinel current-epoch 0

当杀死 master 时,sentinel 将会从新选举新的 master

重新选举

我们进入 6381 查看状态,6381 已经是 master了 并且拥有一个 6382 的slave

127.0.0.1:6381> info Replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6382,state=online,offset=405135,lag=0
master_replid:2fb1ec7df33d87f7489d451e2a4b1ba379c16038
master_replid2:f2291a4914b906e8ce6b88203d755ed9b539ab0a
master_repl_offset:405135
second_repl_offset:58660
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:4800
repl_backlog_histlen:400336

重新启动 6379 后,并且将 6379 设置为 6381 的 slave。

29330:X 25 Sep 17:32:11.190 * +slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6381
29330:X 25 Sep 17:32:41.251 # +sdown slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6381
29330:X 25 Sep 17:32:41.500 # -sdown slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6381
sentinel 在 laravel 中使用
<?php
       $sentinels = [
            'tcp://127.0.0.1:26379',
            'tcp://127.0.0.1:26381',
            'tcp://127.0.0.1:26382',
        ];
        $options = ['replication' => 'sentinel', 'service' => 'mymaster'];

        $redis = new \Predis\Client($sentinels, $options);

        $redis->set('name','jack');
        dd($redis->info());

sentinel 几个常用命令

SENTINEL masters   #查看主节点信息
SENTINEL sentinels <MASTER_NAME> # 查看其他 sentinel 信息
SENTINEL slaves <MASTER_NAME>  #查看对应集群的从节点信息
SENTINEL failover <MASTER_NAME>  #进行故障转移
SENTINEL get-master-addr-by-name <MASTER_NAME> #查看当前的主节点地址

参考: Redis哨兵模式(sentinel)学习总结及部署记录(主从复制、读写分离、主从切换) - 散尽浮华 - 博客园

Redis 哨兵使用以及在 Laravel 中的配置 | Laravel China 社区

Redis Sentinel服务配置 - 漫天雪_昆仑巅 - CSDN博客

Copyright © Kagami丶 2019 all right reserved,powered by Gitbook该文件修订时间: 2019-12-10 21:46:19

results matching ""

    No results matching ""