Redis-Sentinel部署和配置

Redis Sentinel Documentation

http://redis.io/topics/sentinel

Sentinel是一个管理多个redis实例的工具(redis的一种高可用方案),它可以实现对redis的监控、通知和自动故障转移。sentinel不断的检测redis实例是否可以正常工作,通过API向其他程序报告redis的状态,如果redis master不能工作,则会自动启动故障转移进程,将其中的一个slave提升为master,其他的slave重新设置新的master实例。也就是说,它提供了如下功能:
监控(Monitoring): Sentinel 会不断地检查你的主实例和从实例是否正常。
通知(Notification): 当被监控的某个 Redis 实例出现问题时, Sentinel 进程可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover): 当一个主redis实例失效时, Sentinel 会开始记性一次failover, 它会将失效主实例的其中一个从实例升级为新的主实例, 并让失效主实例的其他从实例改为复制新的主实例; 而当客户端试图连接失效的主实例时, 集群也会向客户端返回新主实例的地址, 使得集群可以使用新主实例代替失效实例。
Redis Sentinel自身也是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程, 这些进程使用流言协议(gossip protocols)来接收关于主Redis实例是否失效的信息, 然后使用投票协议来决定是否执行自动failover,以及评选出从Redis实例作为新的主Redis实例。

Quick Start

当前Redis stable版已经自带了redis-sentinel这个工具。另外2.6的redis版本就开始支持sentinel不过推荐使用2.8之后的redis版本,虽然 Redis Sentinel 已经提供了一个单独的可执行文件 redis-sentinel,但实际上它只是一个运行在特殊模式下的 Redis实例,你可以在启动一个普通 Redis实例时通过给定 –sentinel 选项来启动 Redis Sentinel 实例。

Running Sentinel

redis-sentinel /path/to/sentinel.conf
等同于
redis-server /path/to/sentinel.conf --sentinel
其中sentinel.conf是redis的配置文件,Redis sentinel会需要写入配置文件来保存sentinel的当前状态。当配置文件无法写入时,Sentinel启动失败。

Configuring Sentinel

port 26329
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel notification-script myredis <script-path>

sentinel 选项的名字 主Redis实例的名字 选项的值
配置文件的格式为:
第一行配置指示 Sentinel 去监视一个名为 myredis 的主redis实例, 这个主实例的 IP 地址为本机地址127.0.0.1 , 端口号为 6379 , 而将这个主实例判断为失效至少需要 2 个 Sentinel 进程的同意,只要同意 Sentinel 的数量不达标,自动failover就不会执行。同时,一个Sentinel都需要获得系统中大多数Sentinel进程的支持, 才能发起一次自动failover, 并预留一个新主实例配置的编号。而当超过半数Redis不能正常工作时,自动故障转移是无效的。
各个选项的功能如下:
down-after-milliseconds 选项指定了 Sentinel 认为Redis实例已经失效所需的毫秒数。
当实例超过该时间没有返回PING,或者直接返回错误, 那么 Sentinel 将这个实例标记为主观下线(subjectively down,简称 SDOWN )。只有一个 Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移: 只有在足够数量的 Sentinel 都将一个实例标记为主观下线之后,实例才会被标记为客观下线(objectively down, 简称 ODOWN ), 这时自动故障迁移才会执行。具体的行为如下:
(1). 每个 Sentinel 每秒一次向它所监控的主实例、从实例以及其他 Sentinel 实例发送一个 PING 命令。当一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。如果一个主实例被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主实例被标记为客观下线。
(2).在一般情况下, 每个 Sentinel 进程会以每 10 秒一次的频率向它已知的所有主实例和从实例发送 INFO 命令。 当一个主实例被 Sentinel实例标记为客观下线时, Sentinel 向下线主实例的所有从实例发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
(3).当没有足够数量的 Sentinel 同意主实例已经下线, 主Redis服务实例的客观下线状态就会被移除。 当主服务器重新向 Sentinel 的PING 命令返回有效回复时, 主服务器的主观下线状态就会被移除。
parallel-syncs 选项指定了在执行故障转移时, 最多可以有多少个从Redis实例在同步新的主实例, 在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长。
尽管复制过程的绝大部分步骤都不会阻塞从实例, 但从redis实例在载入主实例发来的 RDB 文件时, 仍然会造成从实例在一段时间内不能处理命令请求: 如果全部从实例一起对新的主实例进行同步, 那么就可能会造成所有从Redis实例在短时间内全部不可用的情况出现。
所以从实例被设置为允许使用过期数据集(参见对 redis.conf 文件中对 slave-serve-stale-data 选项),可以缓解所有从实例都在同一时间向新的主实例发送同步请求的负担。你可以通过将这个值设为 1 来保证每次只有一个从Redis实例处于不能处理命令请求的同步状态。
failover-timeout如果在该时间(ms)内未能完成failover操作,则认为该failover失败。
notification-script 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,但是很常用。

Sentinel Cluster Operating Mecha

一个 Sentinel进程可以与其他多个 Sentinel进程进行连接, 每个 Sentinel进程之间可以互相检查对方的可用性, 并进行信息交换。
和其他集群不同的是,你无须设置其他Sentinel的地址,Sentinel进程可以通过发布与订阅来自动发现正在监视相同主实例的其他Sentinel。同样,你也不必手动列出主实例属下的所有从实例,因为Sentinel实例可以通过询问主实例来获得所有从实例的信息。
每个 Sentinel 都订阅了被它监视的所有主服务器和从服务器的 __sentinel__:hello 频道, 查找之前未出现过的 sentinel进程。 当一个 Sentinel 发现一个新的 Sentinel 时,它会将新的 Sentinel 添加到一个列表中,这个列表保存了 Sentinel 已知的,监视同一个主服务器的所有其他Sentinel。

环境部署结构图:

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2

Redis Installation

http://redis.io/download

Configuration Redis and Sentinel

M1:

# grep ^[a-Z] /redis/conf/6379.conf
daemonize yes
pidfile /redis/pid/redis_6379.pid
port 6379
tcp-backlog 511
bind 192.168.1.122 127.0.0.1
timeout 0
tcp-keepalive 0
loglevel notice
logfile /redis/logs/redis.log
databases 16
save 900 1
save 300 300
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /redis/data/
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

S1:

# cat /redis/conf/sentinel.conf
port 6381
pidfile "/redis/pid/redis-sentinel.pid"
dir "/redis/redis-sentinel/tmp"
daemonize yes
loglevel debug
logfile "/redis/logs/sentinel.log"
sentinel monitor redis01 192.168.1.122 6379 1
sentinel down-after-milliseconds redis01 5000
sentinel failover-timeout redis01 900000
sentinel config-epoch redis01 0

R2:

# grep ^[a-Z] /redis/conf/6379.conf
daemonize yes
pidfile /redis/pid/redis_6379.pid
port 6379
tcp-backlog 511
bind 192.168.1.141 127.0.0.1
timeout 0
tcp-keepalive 0
loglevel notice
logfile "/redis/logs/redis_6379.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /redis/data/
slaveof 172.16.34.122	6379
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

S2:

# cat /redis/conf/sentinel.conf
port 6381
pidfile "/redis/pid/redis-sentinel.pid"
dir "/redis/redis-sentinel/tmp"
daemonize yes
loglevel debug
logfile "/redis/logs/sentinel.log"
sentinel monitor redis01 192.168.1.122 6379 1
sentinel down-after-milliseconds redis01 5000
sentinel failover-timeout redis01 900000
sentinel config-epoch redis01 0

R3:

daemonize yes
pidfile /redis/pid/redis_6379.pid
port 6379
tcp-backlog 511
bind 192.168.1.165 127.0.0.1
timeout 0
tcp-keepalive 0
loglevel notice
logfile "/redis/logs/redis_6379.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /redis/data/
slaveof 172.16.34.122	6379
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

S3:

# cat /redis/conf/sentinel.conf
port 6381
pidfile "/redis/pid/redis-sentinel.pid"
dir "/redis/redis-sentinel/tmp"
daemonize yes
loglevel debug
logfile "/redis/logs/sentinel.log"
sentinel monitor redis01 192.168.1.122 6379 1
sentinel down-after-milliseconds redis01 5000
sentinel failover-timeout redis01 900000
sentinel config-epoch redis01 0

启动redis和sentinel

M1:
su -c "/redis/bin/redis-server /redis/etc/6379.conf" redis
R2:
su -c "/redis/bin/redis-server /redis/etc/6379.conf" redis
R3:
su -c "/redis/bin/redis-server /redis/etc/6379.conf" redis

S1:
su -c "/redis/bin/redis-server /redis/conf/sentinel.conf --sentinel" redis
S2:
su -c "/redis/bin/redis-server /redis/conf/sentinel.conf --sentinel" redis
S3:
su -c "/redis/bin/redis-server /redis/conf/sentinel.conf --sentinel" redis

检查和Sentinel常用命令:

M1:
# redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.165,port=6379,state=online,offset=404078566001,lag=1
slave1:ip=192.168.1.141,port=6379,state=online,offset=404078567699,lag=1
master_repl_offset:404078570460
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:404077521885
repl_backlog_histlen:1048576

# redis-cli -h 192.168.1.122 -p 6381 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=redis01,status=ok,address=192.168.1.122:6379,slaves=2,sentinels=3

R2:
# redis-cli info replication
# Replication
role:slave
master_host:192.168.1.122
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:404082481352
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# redis-cli -h 192.168.1.141 -p 6381 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=redis01,status=ok,address=192.168.1.122:6379,slaves=2,sentinels=3

R3:
# redis-cli info replication
# Replication
role:slave
master_host:192.168.1.122
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:404082530096
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# redis-cli -h 192.168.1.165 -p 6381 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=redis01,status=ok,address=192.168.1.122:6379,slaves=2,sentinels=3
Sentinel相关命令:
(1). PING :返回 PONG 。 (2). SENTINEL masters :列出所有被监视的主Redis服务实例,以及这些主服务实例的当前状态。SENTINEL slaves :列出给定主服务实例的所有从实例,以及这些从实例的当前状态。 (3). SENTINEL get-master-addr-by-name : 返回给定名字的主实例的 IP 地址和端口号。 如果这个主实例正在执行故障转移操作, 或者针对这个主实例的故障转移操作已经完成, 那么这个命令返回新的主服务器的 IP 地址和端口号。 (4). SENTINEL reset : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。 重置操作清除该sentinel的所保存的所有状态信息,并进行一次重新的发现过程。 (5). SENTINEL failover :进行一次主动的failover。即在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移 。发起故障转移的 Sentinel 会向其他 Sentinel 发送一个新的配置,其他 Sentinel 会根据这个配置进行相应的更新。

Sentinel Clients

如果要做到应用程序(客户端)对Redis的failover透明Transparent),客户端需要监控sentinel的频道信息,并自动连接新的主节点。官方提供了一个专门的topic来讲解这个问题:Guidelines for Redis clients with support for Redis Sentinel,而一些常用的开发语言也已经有了整合sentinel的redis driver:
Node.JS redis-sentinel-client — Transparent Redis Sentinel client
Python redis 2.10.3
Java Jedis
以Python为例,首先安装redis-py。
pip install redis
一个简单的测试代码如下,首先获得一个Sentinel对象,然后

from redis.sentinel import Sentinel
sentinel = Sentinel([('192.168.1.141', 6381)], socket_timeout=0.1)
sentinel.discover_master('redis01')
sentinel.discover_slaves('redis01')
master = sentinel.master_for('redis01', socket_timeout=0.1)
slave = sentinel.slave_for('redis01', socket_timeout=0.1)
slave.get('t:q:76358803b9012b8d4a20cc3b643864b1')
master.get('t:q:76358803b9012b8d4a20cc3b643864b1')

由于我们这里有一组Sentinel对象,所以需要获取到一组Sentinel对象信息:

from redis.sentinel import Sentinel
sentinel = Sentinel([('192.168.1.141', 6381),('192.168.1.165', 6381),('192.168.1.122', 6381)], socket_timeout=0.1)
sentinel.discover_master('redis01')
sentinel.discover_slaves('redis01')
master = sentinel.master_for('redis01', socket_timeout=0.1)
slave = sentinel.slave_for('redis01', socket_timeout=0.1)
slave.get('t:q:76358803b9012b8d4a20cc3b643864b1')
master.get('t:q:76358803b9012b8d4a20cc3b643864b1')

 

你可能感兴趣的:(Redis-Sentinel部署和配置)