一、前言
单点的redis如果崩溃或者宕机了,是很容易造成程序问题的。所以需要高可用的redis方案。sentinel是常用的redis高可用方案之一,本篇文章将记录整个搭建过程。
二、图解
我采用的是一主二从三哨兵,如图:
三、关于 Redis Sentinel 的概念
Redis Sentinel(译为“哨兵”)是 Redis 官方推荐的高可用性(HA)解决方案,当用 Redis 做 Master-slave 的高可用方案时,假如 master 宕机了,Redis 本身(包括它的很多客户端)都没有实现自动进行主备切换,而 Redis-sentinel 本身也是一个独立运行的进程,它能监控多个 master-slave 集群,发现 master 宕机后能进行自动切换。
Redis Sentinel 系统用于管理多个 Redis 服务器(instance),该系统执行以下三个任务:
- 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification):当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。
Redis Sentinel 是一个分布式系统,你可以在一个架构中运行多个 Sentinel 进程(progress),这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
一个 Sentinel 进程可以与其他多个 Sentinel 进程进行连接,每个 Sentinel 进程之间可以互相检查对方的可用性,并进行信息交换。
四、 搭建 Redis Server(master)
我们就按照上面的架构图进行搭建,大概三台服务器(防火墙关闭):
因为我没有三台服务器,我都装在一台上,模拟在三台上:
47.xxx.xx.102: master redis-server-6379,redis-sentinel -26379
47.xxx.xx.102: slave redis-server-6380 ,redis-sentinel-26380
47.xxx.xx.102: slave redis-server-6381, redis-sentinel-26381
下载redis包,解压到相应的目录,修改配置。
master-6379机器redis配置:
#bind 127.0.0.1,这样的话,访问redis服务只能通过本机的客户端连接,而无法通过远程连接
#bind 127.0.0.1
#保护模式,默认开启,分两种:1、关闭protected-mode模式 2、配置bind或者设置密码
protected-mode yes
#端口
port 6379
#请求密码
requirepass "123456"
tcp-backlog 511
#当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能
timeout 0
tcp-keepalive 300
# 使用守护进程模式
daemonize yes
supervised no
#当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
pidfile "/var/run/redis_6379.pid"
#指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warningnotice生产常用等级,默认为 notice
loglevel notice
#日志文件路径
logfile "/usr/local/redis/redis-4.0.1_6379/log/redis_6379.log"
databases 16
always-show-logo yes
#RDB配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
#指定本地数据库文件名,默认值为 dump.rdb
dbfilename "dump.rdb"
#指定本地数据库存放目录
dir "/usr/local/redis/redis-4.0.1_6379/data"
slave-6380机器redis配置:
protected-mode no
port 6380
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile "/var/run/redis_6379.pid"
loglevel notice
logfile "/usr/local/redis/redis-4.0.1_6380/redis_6380.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
dir "/usr/local/redis/redis-4.0.1_6380/data"
#设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步
slaveof 47.xx.xx.102 6379
requirepass "123456"
# 设置访问 master 的密码
masterauth 123456
slave-6381机器redis配置:
protected-mode no
port 6381
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile "/var/run/redis_6381.pid"
loglevel notice
logfile "/usr/local/redis/redis-4.0.1_6381/redis_6381.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
dir "/usr/local/redis/redis-4.0.1_6381/data"
slaveof 47.110.12.102 6379
requirepass "123456"
# 设置访问 master 的密码
masterauth 123456
依次启动不同服务器上的三台redis服务器:
./redis-server etc/redis_6379.config
./redis-server etc/redis_6380.config
./redis-server etc/redis_6381.config
查看主从信息:INFO replication(因为写文章时,sentinel已经搭建好了,且进行过故障转移,所以当前master是端口为6381的机器)
slave角色redis信息:
[root@iZbp1i9fu1a7yjd42innezZ etc]# ../bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456
127.0.0.1:6379> INFO replication
# Replication
role:slave
master_host:47.110.12.102
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:104705046
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:48c11566ab36630e66aeabfa67192368a12fd66c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:104705046
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:103656471
repl_backlog_histlen:1048576
master角色redis信息:
[root@iZbp1i9fu1a7yjd42innezZ etc]# ../bin/redis-cli -h 127.0.0.1 -p 6381 -a 123456
127.0.0.1:6381> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=47.110.12.102,port=6379,state=online,offset=104777860,lag=1
slave1:ip=47.110.12.102,port=6380,state=online,offset=104777860,lag=1
master_replid:48c11566ab36630e66aeabfa67192368a12fd66c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:104777860
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:103729285
repl_backlog_histlen:1048576
五、 搭建 Redis Server(slave)
接着,我们分别在三台服务器上,配置 Redis Sentinel,创建redis-4.0.1/sentinel-26379.conf(sentinel-26380.conf、sentinel-26381.conf)
配置文件,示例配置(记得更改不同的bind ip
):
sentinel-26379.conf:
port 26379
daemonize yes
protected-mode no
logfile "/usr/local/redis/redis-4.0.1_6379/log/sentinel_26379.log"
dir "/usr/local/redis/redis-4.0.1_6379/temp"
sentinel monitor mymaster 47.110.xx.xxx 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 15000
sentinel auth-pass mymaster 123456
sentinel parallel-syncs mymaster 1
这四行配置为一组,因为我们只有一个 master 节点,所以只配置了一个 master,可以配置多个 master,不用配置 slave 的信息,因为 slave 能够被自动检测到(master 节点会有关于 slave 的消息)
第一行配置指示 Sentinel 去监视一个名为manager1
的主服务器,这个主服务器的 IP 地址为10.9.10.154
,端口号为 6379,而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意(只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
选项说明:
auth-pass
:选项指定了 master 的连接密码。down-after-milliseconds
:选项指定了 Sentinel 认为服务器已经断线所需的毫秒数。failover-timeout
:如果在该时间(ms)内未能完成 failover 操作,则认为该 failover 失败。parallel-syncs
:选项指定了在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长。
sentinel-26380.conf:
port 26380
daemonize yes
protected-mode no
logfile "/usr/local/redis/redis-4.0.1_6380/sentinel_26380.log"
dir "/usr/local/redis/redis-4.0.1_6380/temp"
sentinel monitor mymaster 47.110.xx.xxx 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 15000
sentinel auth-pass mymaster 123456
sentinel parallel-syncs mymaster 1
sentinel-26379.conf:
port 26381
daemonize yes
protected-mode no
logfile "/usr/local/redis/redis-4.0.1_6381/sentinel_26381.log"
dir "/usr/local/redis/redis-4.0.1_6381/temp"
sentinel monitor mymaster 47.110.xx.xxx 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 15000
sentinel auth-pass mymaster 123456
sentinel parallel-syncs mymaster 1
依次启动不同服务器上的sentinel:
../bin/redis-server sentinel-26379.conf --sentinel
../bin/redis-server sentinel-26380.conf --sentinel
../bin/redis-server sentinel-26381.conf --sentinel
当有其他服务器启动 Redis Sentinel 的时候,会有这样的日志(Redis Sentinel 是会相互通信的):
24113:X 20 Oct 02:36:47.607 * +sentinel sentinel 39e2a84573443574f8cf5839aa92509ca1878a16 127.0.0.1 26380 @ mymaster 47.110.12.102 6379
六、Redis Sentinel 故障转移测试
测试当前master角色为:redis-6381
首先kill掉当前master角色redis-6381进程,或者执行redis-cli shutdown命令:
sentinel_26379日志(sentinel_26380、sentinel_26381日志一致):
24113:X 20 Oct 16:32:36.709 +sdown master mymaster 47.110.12.102 6381
24113:X 20 Oct 16:32:36.709 # +new-epoch 418143
24113:X 20 Oct 16:32:36.711 # +vote-for-leader 95f44f2cb9f4b2a0dfdb51db0d0e405974352052 418143
24113:X 20 Oct 16:32:36.941 # +odown master mymaster 47.110.12.102 6381 #quorum 2/2
24113:X 20 Oct 16:32:36.941 # Next failover delay: I will not start a failover before Tue Oct 20 16:33:07 2020
24113:X 20 Oct 16:32:37.470 * +sentinel-address-switch master mymaster 47.110.12.102 6381 ip 172.16.134.91 port 26380 for 39e2a84573443574f8cf5839aa92509ca1878a16
24113:X 20 Oct 16:32:37.503 # -odown master mymaster 47.110.12.102 6381
24113:X 20 Oct 16:32:37.576 # +config-update-from sentinel 95f44f2cb9f4b2a0dfdb51db0d0e405974352052 172.16.134.91 26381 @ mymaster 47.110.12.102 6381
24113:X 20 Oct 16:32:37.576 # +switch-master mymaster 47.110.12.102 6381 47.110.12.102 6380
翻译一下就是:
- 每个 Sentinel 发现了主节点挂掉了并有一个 +sdown 事件
- 这个事件稍候升级到 +odown,意味着大多数 Sentinel 已经同意了主节点是不可达的。
- Sentinels 开始投票一个 Sentinel 开始并尝试故障转移
- 故障转移开始
另外,需要注意的是,Redis Sentinel 并不是提供对外服务的地址,它只是管理 Redis 主备切换的监测工具,所以,对外 Client 提供的地址,仍是 Redis Server 的地址(包含 salve),当然,也可以像提供负载均衡(SLB)或者虚拟IP(Virtual IP,VIP),进行统一地址的访问。
七、springboot项目中如何连接Redis哨兵模式
1.引入springboot-redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.properties配置redis哨兵模式相关参数
# database name
spring.redis.database=0
# server password 密码,如果没有设置可不配
spring.redis.password=123456
# pool settings ...池配置
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
# name of Redis server 哨兵监听的Redis server的名称
spring.redis.sentinel.master=mymaster
# comma-separated list of host:port pairs 哨兵的配置列表
spring.redis.sentinel.nodes=47.110.12.102:26379,47.110.12.102:26380,47.110.12.102:26381
3.代码中测试
@Autowired
RedisTemplate redisTemplate;
@Test
public void setKey(){
redisTemplate.opsForValue().set("test100","100");
System.out.println("############"+redisTemplate.opsForValue().get("test100"));
}