Redis集群-1

V1.1.1_201908_BoBo

课程目标

  • 熟悉Redis在Linux中的单机部署
  • 了解Redis性能测试的指标
  • 实现Redis的读写分离的部署
  • 理解Redis主从复制的数据同步原理
  • 理解高可用的概念
  • 实现Redis故障转移-主从的手动切换
  • 实现Redis故障转移-主从的自动切换-哨兵
  • SpringDataRedis实现哨兵的连接和操作

1. 准备工作

纯净版的Redis的安装

安装环境:Centos7.6.1810

(1)在虚拟机中安装C++环境:

#安装C++编译器(如果已经安装,该步骤可以忽略)
yum install -y gcc-c++
#检验是否成功:
g++ -v

(2)从Redis官网(https://redis.io/)下载稳定版本,本文下载的是5.0.5的版本:redis-5.0.5.tar.gz

(3)安装Redis,依次执行以下命令:

#上传redis-5.0.5.tar.gz到linux中,默认是root目录
# 解压
tar -zxf redis-5.0.5.tar.gz
# 进入解压目录
cd redis-5.0.5
# 编译
make
# 安装,到/usr/local/redis
make install PREFIX=/usr/local/redis

(4)给新安装好的Redis添加默认的配置文件,

# 进入安装好的redis目录
cd /usr/local/redis/bin
# 复制默认的配置文件到redis安装目录
cp /root/redis-5.0.5/redis.conf ./

提示:

上述redis的bin目录为纯净版的redis,备用。

Redis单机实例的启动连接

目标:在Linux中部署启动一个单机版的Redis服务

将纯净版的Redis复制一份,作为新的redis的目录:

# 进入到之前安装好的单机redis目录
cd /usr/local/redis
# 将bin复制出一份,作为一个实例端口为默认的6379
cp -R bin redis-6379

修改配置文件

# 进入主库
cd redis-6379
# 修改配置文件
vi redis.conf

部分配置参考如下:

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
# Redis后台启动,默认值是no
daemonize yes

# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
# Redis服务器可以跨网络访问,默认值是127.0.0.1
bind 0.0.0.0

# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
# 开启aof持久化,默认值是no
appendonly yes

保存配置文件。

提示:

在命令状态下,使用/来进行查找单词

(5)启动Redis:

# 启动redis
./redis-server redis.conf

运行结果:

[root@bobohost redis-6379]# ./redis-server redis.conf
42862:C 28 Jul 2019 08:58:18.427 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
42862:C 28 Jul 2019 08:58:18.427 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=42862, just started
42862:C 28 Jul 2019 08:58:18.427 # Configuration loaded

Redis默认端口6379,查看:

ps -ef | grep -i redis

运行结果:

[root@bobohost redis-6379]# ps -ef |grep -i redis
polkitd   32797  32780  0 07:07 ?        00:00:54 redis-server *:6379
root      42994  36178  0 09:00 pts/1    00:00:00 grep --color=auto -i redis

本地客户端连接测试和退出:

[root@bobohost redis-6379]# ./redis-cli 
127.0.0.1:6379> exit
[root@bobohost bin]#

远程客户端连接(比如使用Windows中的客户端连接):

redis-cli -h 192.168.40.134

【提示】

如果远程连接不上,则需要关闭linux的防火墙或添加访问规则:

关闭Centos7的防火墙

systemctl stop firewalld.service            #停止firewall
systemctl disable firewalld.service        #禁止firewall开机启动

【另外】

Redis的关闭:

方式一:正常关闭,数据保存。

#先登录,命令关闭
192.168.40.141:6379> shutdown
#或者不登陆,直接命令关闭
./redis-cli shutdown

方式二:杀进程,非正常关闭,会造成数据丢失,一般不用。

kill -2 32797

kill -9 32797

其中,32797是redis进程的PID。

2. 读写分离-主从复制

单机Redis的读写速度非常快,能够支持大量用户的访问。虽然Redis的性能很高,但是对于大型网站来说,每秒需要获取的数据远远超过单台redis服务所能承受的压力,所以我们迫切需要一种方案能够解决单台Redis服务性能不足的问题。

性能指标衡量的三个概念

在描述系统的高并发能力时,RT、TPS、QPS经常提到,我们先了解这些概念:

  • RT:响应时间
  • TPS:吞吐量
  • QPS:每秒查询率

(1)响应时间RT

响应时间是指系统对请求作出响应的时间。

直观上看,这个指标与人对软件性能的主观感受是非常一致的,因为它完整地记录了整个计算机系统处理请求的时间。由于一个系统通常会提供许多功能,而不同功能的业务逻辑也千差万别,因而不同功能的响应时间也不尽相同。

在讨论一个系统的响应时间时,通常是指该系统所有功能的平均时间或者所有功能的最大响应时间

(2)吞吐量TPS

吞吐量是指系统在单位时间内处理请求的数量。

对于一个多用户的系统,如果只有一个用户使用时系统的平均响应时间是t,当有你n个用户使用时,每个用户看到的响应时间通常并不是n×t,而往往比n×t小很多。这是因为在处理单个请求时,在每个时间点都可能有许多资源被闲置,当处理多个请求时,如果资源配置合理,每个用户看到的平均响应时间并不随用户数的增加而线性增加。

实际上,不同系统的平均响应时间随用户数增加而增长的速度也不大相同,这也是采用吞吐量来度量并发系统的性能的主要原因。一般而言,吞吐量是一个比较通用的指标,两个具有不同用户数和用户使用模式的系统,如果其最大吞吐量基本一致,则可以判断两个系统的处理能力基本一致。

(3)每秒查询率QPS

每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在互联网中,经常用每秒查询率来衡量服务器的性能。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。

测算Redis性能

redis-benchmark是官方自带的Redis性能测试工具,用来测试Redis在当前环境下的读写性能。我们在使用Redis的时候,服务器的硬件配置、网络状况、测试环境都会对Redis的性能有所影响,我们需要对Redis实时测试以确定Redis的实际性能。

语法:

redis-benchmark [选项] [选项值]

https://redis.io/topics/benchmarks

选项:

选项

描述

默认值

-h

指定服务器主机名

127.0.0.1

-p

指定服务器端口

6379

-s

指定服务器 socket

-c

指定并发连接数

50

-n

指定请求数

10000

-d

以字节的形式指定 SET/GET 值的数据大小

3 Bytes

-t

仅运行以逗号分隔的测试命令列表。

-k

1=keep alive 0=reconnect

1

-r

SET/GET/INCR 使用随机 key, SADD 使用随机值

-P

通过管道传输 请求

1

-q

强制退出 redis。仅显示 query/sec 值

–csv

以 CSV 格式输出

-l

生成循环,永久执行测试

-I

Idle 模式。仅打开 N 个 idle 连接并等待。

使用Redis客户端执行命令,测试性能:

#进入到redis目录
cd ./redis-6379
# 执行测试性能命令
./redis-benchmark -t set,get -n 100000

提示:如无特别说明,所有操作都在linux下进行。

执行结果如下:

[root@bobohost bin]# ./redis-benchmark -t set,get -n 100000
====== SET ======
  100000 requests completed in 1.20 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.38% <= 1 milliseconds
99.73% <= 2 milliseconds
99.86% <= 3 milliseconds
99.97% <= 4 milliseconds
100.00% <= 4 milliseconds
83402.84 requests per second

====== GET ======
  100000 requests completed in 1.24 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.43% <= 1 milliseconds
99.66% <= 2 milliseconds
99.83% <= 3 milliseconds
99.98% <= 4 milliseconds
100.00% <= 4 milliseconds
80515.30 requests per second

在上面的测试结果中,我们关注GET结果最后一行 80515.30 requests per second ,即每秒GET命令处理80515.30个请求,即QPS8.0万。但这里的数据都只是理想的测试数据,测出来的QPS不能代表实际生产的处理能力。

在实际生产中,我们需要关心在应用场景中,redis能够处理的最大的(QPS/TPS)是多少。我们需要估计生产的报文大小,使用benchmark工具指定-d数据块大小来模拟:

./redis-benchmark -t get -n 100000 -c 100 -d 2048

测试结果:

[root@bobohost bin]# ./redis-benchmark -t get -n 100000 -c 100 -d 2048
====== GET ======
  100000 requests completed in 1.25 seconds
  100 parallel clients
  2048 bytes payload
  keep alive: 1

96.17% <= 1 milliseconds
99.53% <= 2 milliseconds
99.81% <= 3 milliseconds
99.96% <= 4 milliseconds
100.00% <= 4 milliseconds
79681.27 requests per second

测得的QPS是7.9万

info命令查看Redis信息:

我们也可以使用redis客户端登陆到redis服务中,执行info命令查看redis的其他信息,执行命令:

# 使用Redis客户端
#./redis-cli -h 192.168.40.141
./redis-cli
# 在客户端中执行info命令
127.0.0.1:6379> info

查看结果(摘取部分结果):

# Clients
connected_clients:1 #redis连接数

# Memory
used_memory:857688 # Redis 分配的内存总量 
used_memory_human:837.59K
used_memory_rss:15245312
used_memory_rss_human:14.54M # Redis 分配的内存总量(包括内存碎片) 
used_memory_peak:6979840
used_memory_peak_human:6.66M #Redis所用内存的高峰值
used_memory_peak_perc:12.29%
used_memory_overhead:843924
used_memory_startup:791368
used_memory_dataset:13764
used_memory_dataset_perc:20.75%

为什么要做Redis读写分离

在前面我们已经测试过,如果只有一台服务器,QPS是8.0万,而在大型网站中,可能要求更高的QPS,很明显,一台服务器就不能满足需要了。

Redis在知乎的规模:
机器内存总量约 70TB,实际使用内存约 40TB。
平均每秒处理约 1500 万次请求,峰值每秒约 2000 万次请求。 #QPS 2000万
每天处理约 1 万亿余次请求。
单集群每秒处理最高每秒约 400 万次请求。

我们可以对读写能力扩展,采用读写分离的方式解决性能瓶颈。运行新的服务器(称为从服务器,slave server),让从服务器与主服务器进行连接,然后主服务器发送数据副本,从服务器通过网络根据主服务器的数据副本进行准实时更新(具体的更新速度取决于网络带宽)。

这样我们就有额外的从服务器处理读请求,通过将读请求分散到不同的服务器上面进行处理, 用户可以从新添加的从服务器上获得额外的读查询处理能力。

读写分离的实现

redis已经发现了这个读写分离场景特别普遍,自身集成了读写分离供用户使用。我们只需在redis的配置文件里面加上一条,slaveof host port语句配置即可,我们现在开始配置主从环境。

目标:一主一从

实现如下步骤:

依次执行下列命令:

# 关闭之前的单机redis(如果启动的话),之前的6379作为主库
/usr/local/redis/redis-6379/redis-cli shutdown
# 进入到之前的redis目录
cd /usr/local/redis
# 将redis-6379复制出一份,作为从库
cp -R redis-6379 redis-6380
# 进入主库
cd redis-6379
# 启动主库
./redis-server redis.conf
#主库什么都没干,上述操作主要是为了复制一份


# 进入从库
cd redis-6380
# 修改从库配置
vi redis.conf
修改 port 为 6380
添加 slaveof 192.168.40.141 6379
# 删除快照文件
rm -f dump.rdb
rm -f appendonly.aof
# 启动从库
./redis-server redis.conf

分别用客户端登录两个服务,通过info命令查看:

6379主

# Replication
role:master
connected_slaves:0
master_replid:30637ae4a1f1548a27772c5a77fe285afceb6cc3
master_replid2:0000000000000000000000000000000000000000

6380从

# Replication
role:slave
master_host:192.168.40.141
master_port:6379

分别连接主库(6379)和从库(6380),测试发现主库的写操作,从库立刻就能看到相同的数据。但是在从库进行写操作,提示 READONLY You can't write against a read only slave 不能写数据到从库。操作参考如下:

主库操作示例参考:

[root@bobohost ~]# cd /usr/local/redis/bin/
[root@bobohost bin]# ./redis-cli 
127.0.0.1:6379> keys *
1) "key:__rand_int__"
127.0.0.1:6379> set username Rose
OK
127.0.0.1:6379> get username
"Rose"
127.0.0.1:6379>

从库操作示例参考:

[root@bobohost ~]# cd /usr/local/redis/bin/
[root@bobohost bin]# ./redis-cli -p 6380
127.0.0.1:6380> keys *
1) "key:__rand_int__"
2) "username"
127.0.0.1:6380> get username
"Rose"
127.0.0.1:6380> set username Jack
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380>

现在我们就可以通过这种方式配置多个从库读操作,主库进行写操作,实现读写分离,以提高redis的QPS。

主从复制数据同步原理

【概念强调】

主从复制:主节点负责写数据,从节点负责读数据,主节点定期把数据同步到从节点保证数据的一致性。

通常,从服务也称之为副本。

主从复制中的主从服务器双方的数据库将保存相同的数据,概念上将这种现象称作数据库状态一致

Redis数据库持久化有两种方式:RDB全量持久化和AOF增量持久化

(1)redis2.8版本之前使用旧版复制功能SYNC,这是一个非常耗费资源的操作(了解)

  • 主服务器需要执行BGSAVE命令来生成RDB文件,这个生成操作会耗费主服务器大量量的的CPU、内存和磁盘读写资源。
  • 主服务器将RDB文件发送给从服务器,这个发送操作会耗费主从服务器大量的网络带宽和流量,并对主服务器响应命令
  • 请求的时间产生影响:接收到RDB文件的从服务

(2)2.8之后使用PSYNC,具有完整重同步和部分重同步两种模式部分重同步两种模式。

第一种完整重同步:

redis 集群 dump文件 redis集群down_redis

第二种部分重同步:

redis 集群 dump文件 redis集群down_redis 集群 dump文件_02

功能由以下三个部分构成:

  • 主服务的复制偏移量(replication offset)和从服务器器的复制偏移量量。
  • 主服务器的复制积压缓冲区(replication backlog),默认大小为1M。
  • 服务器器的运行ID,用于存储服务器器标识:
    如果从服务器断线重新连接,获取主服务器的运行ID与重接后的主服务器运行ID进行对比,
    判断是不是原来的主服务器,从而决定是执行部分重同步,还是执行完整重同步

通过上面的例子,我们知道redis的主从复制,主服务器执行写操作命令,从服务器会通过主服务器的数据的变化,同步数据到从服务器。但是如果主服务器下线,或者从服务器无法连接主服务器,那么数据同步该如何拿到不能连接主服务器这段时间的命令呢?

【测试】

分别关闭和启动6379和6380的服务来测试主从复制的数据同步。

3. 高可用-哨兵

高可用介绍

高可用是分布式系统架构设计中必须考虑的因素之一,它是通过架构设计减少系统不能提供服务的时间。保证高可用通常遵循下面几点:

  1. 单点是系统高可用的大敌,应该尽量在系统设计的过程中避免单点。
  2. 通过架构设计而保证系统高可用的,其核心准则是:冗余。
  3. 每次出现故障需要人工介入恢复,会增加系统不可用的时间,实现自动故障转移。

我们现在已经给Redis实现了主从复制,可将主节点数据同步给从节点,从节点此时有两个作用:

  1. 从节点扩展主节点的读能力,分担主节点读压力。
  2. 一旦主节点宕机,从节点作为主节点的备份可以随时顶上来。(高可用)

因此,已经实现了冗余、避免了单点故障,但缺少故障转移机制。

故障转移-主从的手动切换

准备一主两从服务

目标:一般至少有三个节点,一主二从。

一旦主节点宕机,就需要把从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工操作。

因为之前已经有了一个主从服务,因此我们需要再准备一个从服务,依次执行以下命令:

# 进入到之前安装好的单机redis目录
cd /usr/local/redis
# 将从节点复制一份
cp -R redis-6380 redis-6381
# 进入从库2
cd redis-6381
# 修改从库2配置
vi redis.conf
修改 port 为 6381
修改(略,如果已经存在,无需修改) slaveof 192.168.40.141 6379
# 删除快照文件
rm -f dump.rdb
rm -f appendonly.aof
# 启动从库2
./redis-server redis.conf

分别进入一主两从服务,执行info命令,看到服务的状态:

主服务器:

# Replication
role:master
connected_slaves:2
slave0:ip=192.168.40.141,port=6380,state=online,offset=2594,lag=1
slave1:ip=192.168.40.141,port=6381,state=online,offset=2594,lag=1

从服务器1:

# Replication
role:slave
master_host:192.168.40.141
master_port:6379
master_link_status:up

从服务器2:

# Replication
role:slave
master_host:192.168.40.141
master_port:6379
master_link_status:up

制造故障-主节点Down机

主服务下线

登录6379端口号的主服务,并执行shutdown命令,关闭这个主redis。

分别进入6380,6380从服务执行info命令,我们可以看到两个从服务的信息都变为:

# Replication
role:slave
master_host:192.168.40.141
master_port:6379
master_link_status:down

可以看到主的状态由原来的up变为down,说明主服务下线了。

主从切换

现在可以把6380升级为主服务,执行命令:

127.0.0.1:6380> slaveof no one
OK

修改6381对应的主服务器,执行命令:

slaveof 192.168.40.141 6380

再次执行info命令,可以看到主从服务器都切换成功。现在变成了一主一从,对外是正常的。

结果参考:

6380的节点:

# Replication
role:master
connected_slaves:1
slave0:ip=192.168.40.141,port=6381,state=online,offset=2804,lag=0

6381的节点:

# Replication
role:slave
master_host:192.168.40.141
master_port:6380
master_link_status:up

思考:如果原主6379恢复了,会发生什么?

答:6379会成为一个单点的服务,需要手动加入到新的主从集群中。

故障自动转移-主从的自动切换-哨兵机制

哨兵机制介绍

Redis哨兵(Sentinel)机制:当主节点出现故障时,由Redis哨兵(Sentinel)自动完成故障发现和转移,并通知应用方,实现高可用性。

哨兵机制的出现是为了解决主从复制的缺点的。

在前面的例子中,主节点宕机,需要把从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点。(热切换、配置文件修改等操作)

这整个过程都是人工,费事费力,还会造成一段时间内服务不可用,而且需要人一直都在。这不是一种好的方式,更多时候,我们优先考虑Sentinel(哨兵)。

Sentinel工作模式:

redis 集群 dump文件 redis集群down_redis 集群 dump文件_03

哨兵使用

哨兵的安装和配置

Sentinel在redis的安装后的目录中就有,我们直接使用就可以了,但是先需要修改配置文件,执行命令:

# 进入到之前安装好的单机redis目录
cd /usr/local/redis
# 将bin复制出一份,作为哨兵软件目录
cp -R bin redis-sentinel
#进入哨兵软件目录
cd redis-sentinel
# 复制sentinel配置文件
cp /root/redis-5.0.5/sentinel.conf sentinel01.conf

了解:老版本的redis的哨兵(redis-sentinel命令),在安装包中。但新版本的直接就在安装好的目录中。

修改哨兵的核心配置文件

# 修改配置文件:
vi sentinel01.conf

在sentinel01.conf配置文件中修改或添加:

# By default Sentinel will not be reachable from interfaces different than
# localhost, either use the 'bind' directive to bind to a list of network
# interfaces, or disable protected mode with "protected-mode no" by
# adding it to this configuration file.
#
# Before doing that MAKE SURE the instance is protected from the outside
# world via firewalling or other means.
#
# For example you may use one of the following:
#
# bind 127.0.0.1 192.168.1.1
# 外部可以访问
bind 0.0.0.0

# port <sentinel-port>
# The port that this sentinel instance will run on
#端口默认26379即可,无需修改
port 26379

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
# Replicas are auto-discovered, so you don't need to specify replicas in
# any way. Sentinel itself will rewrite this configuration file adding
# the replicas using additional configuration options.
# Also note that the configuration file is rewritten when a
# replica is promoted to master.
#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".
#sentinel monitor mymaster 127.0.0.1 6379 1
#设置主节点名字,以及将主服务器判断为失效需要投票的哨兵数量,这里设置至少需要 1个哨兵同意。
sentinel monitor mymaster 192.168.40.141 6379 1
# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached replica or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
#
# Default is 30 seconds.
#sentinel down-after-milliseconds mymaster 30000
#设置Sentinel认为服务器已经断线所需的毫秒数为10秒(仅仅为了测试方便,改小了)
sentinel down-after-milliseconds mymaster 10000


# sentinel parallel-syncs <master-name> <numreplicas>
#
# How many replicas we can reconfigure to point to the new replica simultaneously
# during the failover. Use a low number if you use the replicas to serve query
# to avoid that all the replicas will be unreachable at about the same
# time while performing the synchronization with the master.
#设置同时只能一台服务器向新的主节点同步数据
sentinel parallel-syncs mymaster 1

# sentinel failover-timeout <master-name> <milliseconds>
#
# Specifies the failover timeout in milliseconds. It is used in many ways:
#
# - The time needed to re-start a failover after a previous failover was
#   already tried against the same master by a given Sentinel, is two
#   times the failover timeout.
#
# - The time needed for a replica replicating to a wrong master according
#   to a Sentinel current configuration, to be forced to replicate
#   with the right master, is exactly the failover timeout (counting since
#   the moment a Sentinel detected the misconfiguration).
#
# - The time needed to cancel a failover that is already in progress but
#   did not produced any configuration change (SLAVEOF NO ONE yet not
#   acknowledged by the promoted replica).
#
# - The maximum time a failover in progress waits for all the replicas to be
#   reconfigured as replicas of the new master. However even after this time
#   the replicas will be reconfigured by the Sentinels anyway, but not with
#   the exact parallel-syncs progression as specified.
#
# Default is 3 minutes.
#sentinel failover-timeout mymaster 180000
#故障转移的过期时间设置为1分钟
sentinel failover-timeout mymaster 60000

注意:如果有sentinel monitor mymaster 127.0.0.1 6379 2配置,则注释掉。

参数说明:

  • sentinel monitor mymaster 192.168.40.141 6379 1
    mymaster 主节点名,可以任意起名,但必须和后面的配置保持一致。
    192.168.40.141 6379 主节点连接地址。
    1 将主服务器判断为失效需要投票,这里设置至少需要 1个 Sentinel 同意。
  • sentinel down-after-milliseconds mymaster 10000
    设置Sentinel认为服务器已经断线所需的毫秒数。
  • sentinel parallel-syncs mymaster 1
    设置在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小,表示同时进行同步的从服务器越少,那么完成故障转移所需的时间就越长。
    如果从服务器允许使用过期数据集, 那么我们可能不希望所有从服务器都在同一时间向新的主服务器发送同步请求, 因为从服务器在载入主服务器发来的RDB文件时, 会造成从服务器在一段时间内不能处理命令请求。如果全部从服务器一起对新的主服务器进行同步, 那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。
  • sentinel failover-timeout mymaster 60000
    设置failover(故障转移)的过期时间。当failover开始后,在此时间内仍然没有触发任何failover操作, 当前sentinel将会认为此次failoer失败。
哨兵的运行

配置文件修改后,执行以下命令,启动sentinel:

[root@bobohost redis-sentinel]# ./redis-sentinel sentinel01.conf

运行结果:

[root@bobohost redis-sentinel]# ./redis-sentinel sentinel01.conf
48453:X 28 Jul 2019 10:44:20.587 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
48453:X 28 Jul 2019 10:44:20.587 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=48453, just started
48453:X 28 Jul 2019 10:44:20.587 # Configuration loaded
48453:X 28 Jul 2019 10:44:20.588 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.5 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 48453
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

48453:X 28 Jul 2019 10:44:20.589 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
48453:X 28 Jul 2019 10:44:20.589 # Sentinel ID is 79e6814e2a4c8760e571a572aa8da654cfc698fc
48453:X 28 Jul 2019 10:44:20.589 # +monitor master mymaster 192.168.40.141 6379 quorum 1
48453:X 28 Jul 2019 10:44:27.558 * +slave slave 192.168.40.141:6380 192.168.40.141 6380 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:44:27.560 * +slave slave 192.168.40.141:6381 192.168.40.141 6381 @ mymaster 192.168.40.141 6379

从日志看到,6379是主服务,6380和6381是从服务。

另外,哨兵的默认端口为26379

Sentinel模式下的几个事件(参考阅读):

  • +reset-master :主服务器已被重置。
  • +slave :一个新的从服务器已经被 Sentinel 识别并关联。
  • +failover-state-reconf-slaves :故障转移状态切换到了 reconf-slaves 状态。
  • +failover-detected :另一个 Sentinel 开始了一次故障转移操作,或者一个从服务器转换成了主服务器。
  • +slave-reconf-sent :领头(leader)的 Sentinel 向实例发送了 SLAVEOF 命令,为实例设置新的主服务器。
  • +slave-reconf-inprog :实例正在将自己设置为指定主服务器的从服务器,但相应的同步过程仍未完成。
  • +slave-reconf-done :从服务器已经成功完成对新主服务器的同步。
  • -dup-sentinel :对给定主服务器进行监视的一个或多个 Sentinel 已经因为重复出现而被移除 —— 当 Sentinel 实例重启的时候,就会出现这种情况。
  • +sentinel :一个监视给定主服务器的新 Sentinel 已经被识别并添加。
  • +sdown :给定的实例现在处于主观下线状态。
  • -sdown :给定的实例已经不再处于主观下线状态。
  • +odown :给定的实例现在处于客观下线状态。
  • -odown :给定的实例已经不再处于客观下线状态。
  • +new-epoch :当前的纪元(epoch)已经被更新。
  • +try-failover :一个新的故障迁移操作正在执行中,等待被大多数 Sentinel 选中(waiting to be elected by the majority)。
  • +elected-leader :赢得指定纪元的选举,可以进行故障迁移操作了。
  • +failover-state-select-slave :故障转移操作现在处于 select-slave 状态 —— Sentinel 正在寻找可以升级为主服务器的从服务器。
  • no-good-slave :Sentinel 操作未能找到适合进行升级的从服务器。Sentinel 会在一段时间之后再次尝试寻找合适的从服务器来进行升级,又或者直接放弃执行故障转移操作。
  • selected-slave :Sentinel 顺利找到适合进行升级的从服务器。
  • failover-state-send-slaveof-noone :Sentinel 正在将指定的从服务器升级为主服务器,等待升级功能完成。
  • failover-end-for-timeout :故障转移因为超时而中止,不过最终所有从服务器都会开始复制新的主服务器(slaves will eventually be configured to replicate with the new master anyway)。
  • failover-end :故障转移操作顺利完成。所有从服务器都开始复制新的主服务器了。
  • +switch-master :配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关心的信息。
  • +tilt :进入 tilt 模式。
  • -tilt :退出 tilt 模式。
制造故障-主节点Down机

我们在6379执行shutdown,关闭主服务,Sentinel提示如下:

#主节点宕机
48453:X 28 Jul 2019 10:50:25.720 # +sdown master mymaster 192.168.40.141 6379 
#quorum 1/1 
48453:X 28 Jul 2019 10:50:25.720 # +odown master mymaster 192.168.40.141 6379 #quorum 1/1
48453:X 28 Jul 2019 10:50:25.720 # +new-epoch 4
#尝试故障转移
48453:X 28 Jul 2019 10:50:25.720 # +try-failover master mymaster 192.168.40.141 6379
#选举领导
48453:X 28 Jul 2019 10:50:25.722 # +vote-for-leader 79e6814e2a4c8760e571a572aa8da654cfc698fc 4
48453:X 28 Jul 2019 10:50:25.722 # +elected-leader master mymaster 192.168.40.141 6379
#故障转移选择从服务
48453:X 28 Jul 2019 10:50:25.722 # +failover-state-select-slave master mymaster 192.168.40.141 6379
#故障转移状态发送 发送到6381
48453:X 28 Jul 2019 10:50:25.786 # +selected-slave slave 192.168.40.141:6381 192.168.40.141 6381 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:25.786 * +failover-state-send-slaveof-noone slave 192.168.40.141:6381 192.168.40.141 6381 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:25.858 * +failover-state-wait-promotion slave 192.168.40.141:6381 192.168.40.141 6381 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:26.422 # +promoted-slave slave 192.168.40.141:6381 192.168.40.141 6381 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:26.422 # +failover-state-reconf-slaves master mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:26.494 * +slave-reconf-sent slave 192.168.40.141:6380 192.168.40.141 6380 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:27.183 * +slave-reconf-inprog slave 192.168.40.141:6380 192.168.40.141 6380 @ mymaster 192.168.40.141 6379
48453:X 28 Jul 2019 10:50:28.250 * +slave-reconf-done slave 192.168.40.141:6380 192.168.40.141 6380 @ mymaster 192.168.40.141 6379
 #故障转移结束,原来的主服务是6379
48453:X 28 Jul 2019 10:50:28.313 # +failover-end master mymaster 192.168.40.141 6379
 #切换主服务,由原来的6379切换为现在的6381
48453:X 28 Jul 2019 10:50:28.313 # +switch-master mymaster 192.168.40.141 6379 192.168.40.141 6381
48453:X 28 Jul 2019 10:50:28.313 * +slave slave 192.168.40.141:6380 192.168.40.141 6380 @ mymaster 192.168.40.141 6381
48453:X 28 Jul 2019 10:50:28.313 * +slave slave 192.168.40.141:6379 192.168.40.141 6379 @ mymaster 192.168.40.141 6381
48453:X 28 Jul 2019 10:50:38.346 # +sdown slave 192.168.40.141:6379 192.168.40.141 6379 @ mymaster 192.168.40.141 6381

根据提示信息,我们可以看到,6379故障转移到了6380,通过投票选择6380为新的主服务器。

在6381执行info

# Replication
role:master
connected_slaves:1
slave0:ip=192.168.40.141,port=6380,state=online,offset=55782,lag=1
master_replid:bb82d602f855dfa0198f18526201d3bcbc3f7355
master_replid2:91285aef3a857d4d0ecc27f60af0332cae1d75b2

在6380执行info

# Replication
role:slave
master_host:192.168.40.141
master_port:6381
master_link_status:up

故障转移结果如下图:

redis 集群 dump文件 redis集群down_Redis_04

原主节点恢复运行

将原主节点6379的服务启动。

刚启动时,6379也是主节点(配置中没有配置为从节点),但很快被哨兵纠正:

#告诉6379,已经有个主节点6381了
48453:X 28 Jul 2019 11:03:22.220 # -sdown slave 192.168.40.141:6379 192.168.40.141 6379 @ mymaster 192.168.40.141 6381
 #在6379上,切换主服务,将6379的主节点切换到6381
48453:X 28 Jul 2019 11:03:32.218 * +convert-to-slave slave 192.168.40.141:6379 192.168.40.141 6379 @ mymaster 192.168.40.141 6381

在6379执行info

# Replication
role:slave
master_host:192.168.40.141
master_port:6381
master_link_status:up

即,即使老领导回来了,还得当小兵。

哨兵判断下线原理

Sentinel主要是监控服务器的状态,并决定是否进行故障转移。如何进行故障转移在前面的部分已经给大家演示过人工的操作,那么Sentinel是如何判断服务是否下线呢,主要分为主观下线和客观下线:

  • 主观下线:
  • 概念:
    主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断
  • 特点:
    如果一个服务器没有在 master-down-after-milliseconds 选项所指定的时间内, 对向它发送 PING 命令的 Sentinel 返回一个有效回复, 那么 Sentinel 就会将这个服务器器标记为主观下线
  • 客观下线
  • 概念:
    多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。 (一个Sentinel 可以通过向另一个 Sentinel 发送命令来
    询问对方是否认为给定的服务器器已下线)
  • 特点:
    从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法(strong quorum algorithm),而是使用了谣言传播(Gossip): 如果Sentinel在给定的时间范围内, 从其他Sentinel那里接收到了足够数量的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。如果之后其他Sentinel 不再报告主服务器已下线, 那么客观下线状态就会被移除。
  • 注意点:
    客观下线条件只适用于主服务器,对于其他类型的 Redis 实例, Sentinel 在将它们判断为下线前不不需要进行协商, 所以从服务器或者其他 Sentinel 不会达到客观下线条件。 只要一个 Sentinel 发现某个主服务器进入了客观下线状态, 这个Sentinel就可能会被其他 Sentinel 推选出,并对失效的主服务器执行自动故障迁移操作。

p.s:选举原理课后查询。

哨兵小结等

Sentinel三大工作任务

  • 监控(Monitoring): Sentinel 会不不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器器出现问题时, Sentinel 可以通过API向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时,Sentinel会开始一次自动故障转移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器。
    当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

互联网冷备和热备

  • 冷备
  • 概念:
    冷备发生在数据库已经正常关闭的情况下,当正常关闭时会提供给我们一个完整的数据库
  • 优点:
  • 非常快速的备份方法(只需拷文件)
  • 低度维护,高度安全
  • 缺点:
  • 单独使用时,只能提供“某一时间点上”的恢复
  • 在实施备份的全过程中,数据库必须要作备份而不能作其他工作。也就是说,在冷备份过程中,数据库必须是关闭状态
  • 热备
  • 概念:
    热备份是在数据库运行的情况下,采用归档模式(archivelog mode)方式备份数据库的方法
  • 优点:
  • 备份的时间短
  • 备份时数据库仍可使用
  • 可达到秒级恢复
  • 缺点:
  • 若热备份不不成功,所得结果不可用于时间点的恢复
  • 难于维护,要非常仔细小心

Redis客户端本地连接哨兵访问

主要用来监控查看节点,以及可以协调节点。

连接哨兵:

[root@bobohost redis-sentinel]# ./redis-cli -p 26379
127.0.0.1:26379>

查看主节点的信息和状态:

127.0.0.1:26379> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "192.168.40.141"
 5) "port"
 6) "6381"
 7) "runid"
 8) "63e1473811c6af128d7ecd5eb775fdf953fdc1e5"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "772"
19) "last-ping-reply"
20) "772"
21) "down-after-milliseconds"
22) "10000"
23) "info-refresh"
24) "6703"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "4485530"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "0"
35) "quorum"
36) "1"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

查看某主节点所有的从节点的信息和状态

127.0.0.1:26379> sentinel slaves mymaster
1)  1) "name"
    2) "192.168.40.141:6379"
    3) "ip"
    4) "192.168.40.141"
    5) "port"
    6) "6379"
    7) "runid"
    8) "75a952d0b2c4cdbfade7b6f5856f72d7e5ee0b05"
    9) "flags"
   10) "slave"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "635"
   19) "last-ping-reply"
   20) "635"
   21) "down-after-milliseconds"
   22) "10000"
   23) "info-refresh"
   24) "3650"
   25) "role-reported"
   26) "slave"
   27) "role-reported-time"
   28) "3979703"
   29) "master-link-down-time"
   30) "0"
   31) "master-link-status"
   32) "ok"
   33) "master-host"
   34) "192.168.40.141"
   35) "master-port"
   36) "6381"
   37) "slave-priority"
   38) "100"
   39) "slave-repl-offset"
   40) "334994"
2)  1) "name"
    2) "192.168.40.141:6380"
    3) "ip"
    4) "192.168.40.141"
    5) "port"
    6) "6380"
    7) "runid"
    8) "ca7fd27227eff1fe0443f8e406495c5a19f451c0"
    9) "flags"
   10) "slave"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "635"
   19) "last-ping-reply"
   20) "635"
   21) "down-after-milliseconds"
   22) "10000"
   23) "info-refresh"
   24) "1048"
   25) "role-reported"
   26) "slave"
   27) "role-reported-time"
   28) "4588828"
   29) "master-link-down-time"
   30) "0"
   31) "master-link-status"
   32) "ok"
   33) "master-host"
   34) "192.168.40.141"
   35) "master-port"
   36) "6381"
   37) "slave-priority"
   38) "100"
   39) "slave-repl-offset"
   40) "335137"

常见的哨兵命令

#显示指定master的信息和状态;
SENTINEL master <master name> 
#显示指定master的所有slave以及它们的状态;
SENTINEL slaves <master name> 

# 重置名字匹配正则表达式的所有master状态信息,清除之前存储的状态信息和slaves信息。PS:节点只要加入过sentinel,信息就会保存而不会自动清除  
sentinel reset <pattern>

# 用于改变关于master的配置,例如 sentinel set mymaster down-after-milliseconds 1000 ,此命令修改了当节点第一次失去连接到判定其下线所经过的时间
sentinel set <name> <option> <value>

# 告诉sentinel去监听新的master 
sentinel monitor <name> <ip> <port> <quorum>  

# 命令sentinel放弃对某个master的监听
sentinel remove <name>  

# 这个参数设置集群从判断节点挂掉,到执行failover操作(即重新选举master节点)的时间
sentinel failover-timeout mymaster 10000

Redis远程客户端认证的访问配置

Redis5.0.5 默认开启了保护模式:

# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
#
# When protected mode is on and if:
#
# 1) The server is not binding explicitly to a set of addresses using the
#    "bind" directive.
# 2) No password is configured.
#
# The server only accepts connections from clients connecting from the
# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain
# sockets.
#
# By default protected mode is enabled. You should disable it only if
# you are sure you want clients from other hosts to connect to Redis
# even if no authentication is configured, nor a specific set of interfaces
# are explicitly listed using the "bind" directive.
protected-mode yes

保护模式是一层安全保护,以避免在Internet上保持打开的Redis实例访问和利用。

当保护模式打开时,如果:服务器没有使用“bind”指令和未配置密码,

默认情况下,服务器只接受来自客户端的连接IPv4和IPv6环回地址127.0.0.1::1,只能从Uninx系统域内访问连接使用,不允许远程连接使用(如此时在Windows下可连接,但无法使用)。

开启远程应用程序连接的方式,

  • 方式1:关闭保护模式。测试环境可以,生产环境不建议。
  • 方式2:开启保护模式。需要设置绑定IP地址,以及设置密码。

本文采用方式2。

将上述三个Redis服务的配置文件redis.conf,都按照下面的参考配置来修改:

# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
# Redis服务器可以跨网络访问,默认值是127.0.0.1
bind 0.0.0.0

# Require clients to issue AUTH <PASSWORD> before processing any other
# commands.  This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
# requirepass foobared
# 设置密码
requirepass 123456

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
# masterauth <master-password>
# 设置副本(从)服务器访问主服务器密码(如果没有从服务器,则无需设置)
masterauth 123456

提示:

如果之前已经修改过的,就可以略过。

先关闭哨兵,重启三个服务。

【测试】

linux本地连接登录某服务节点:

[root@bobohost bin]# ./redis-cli -h 192.168.40.141 -p 6381 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.40.141:6381>

其中-a 就是设置访问密码

远程连接登录某服务节点测试,在Windows下的连接:

redis-cli -h 192.168.40.141 -p 6381 -a 123456

【提示】

如果连接不上,则需要关闭linux的防火墙或添加访问规则:

关闭Centos7的防火墙

systemctl stop firewalld.service            #停止firewall
systemctl disable firewalld.service        #禁止firewall开机启动

哨兵配置:

因为主节点配置了密码,因此,哨兵连接主节点也需要认证,配置的部分如下:

vim sentinel01.conf

# sentinel iauth-pass <master-name> <password>
#
# Set the password to use to authenticate with the master and replicas.
# Useful if there is a password set in the Redis instances to monitor.
#
# Note that the master password is also used for replicas, so it is not
# possible to set a different password in masters and replicas instances
# if you want to be able to monitor these instances with Sentinel.
#
# However you can have Redis instances without the authentication enabled
# mixed with Redis instances requiring the authentication (as long as the
# password set is the same for all the instances requiring the password) as
# the AUTH command will have no effect in Redis instances with authentication
# switched off.
#
# Example:
#
# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
sentinel auth-pass mymaster 123456

注意:这个配置有个坑。启动可能会报错:

[root@bobohost redis-sentinel]# ./redis-sentinel sentinel01.conf 

*** FATAL CONFIG FILE ERROR ***
Reading the configuration file, at line 105
>>> 'sentinel auth-pass mymaster 123456'
No such master with specified name.

错误原因说是找不到指定的名字的master。

解决方案:

将这段配置放到监控主机的配置下面:

sentinel monitor mymaster 192.168.40.141 6379 1
sentinel auth-pass mymaster 12345

连接哨兵(可选):

redis-cli -h 192.168.40.141 -p 26379
192.168.40.141:26379>

SpringDataRedis连接哨兵

目标:使用Java的客户端连接Redis。这里使用的客户端是SpringDataRedis的框架,它封装很多的细节,使用非常方便。

在配置的时候,一旦有哨兵,要连接哨兵,具体操作哪个具体redis,都由框架完成。

新建SpringBoot的独立的Maven测试工程redis_demo,部分参考配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itcast</groupId>
    <artifactId>redis_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--  继承  -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

</project>

编写配置文件application.yml:

spring:
  redis:
    password: 123456
    #redis哨兵模式配置
    sentinel:
      # 主节点名字
      master: mymaster
      # 哨兵服务地址和端口,如果有多个哨兵,则用英文逗号分割
      nodes: 192.168.40.141:26379
logging:
  level:
    #根路径包的日志级别,默认就是info
    root: info
    #配置指定的包的日志级别
    org.springframework.data.redis: debug
  #指定log4j的日志核心配置文件
  #config: classpath:log4j.properties
  #日志同时写入到磁盘文件中
  file: d:/redisdemo.log

注意这里不需要配置master的host和port,这些信息会从Redis Sentinel中得到。

编写启动类:

cn.itcast.demo.RedisApplication

package cn.itcast.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class, args);
    }
}

编写测试类:

cn.itcast.demo.RedisTemplateTest

package cn.itcast.demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Collection;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisApplication.class)
public class RedisTemplateTest {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    public void test() {
        //增加测试数据
        redisTemplate.opsForValue().set("username", "XiaoMing");
        //读取数据
        String username = redisTemplate.opsForValue().get("username");
        System.out.println("username:"+username);

        Collection<RedisServer> masters = redisTemplate.getConnectionFactory().getSentinelConnection().masters();
        for (RedisServer master : masters) {
            //打印主节点
            System.out.println("============主节点===============");
            System.out.println(master);

            Collection<RedisServer> slaves = redisTemplate.getConnectionFactory().getSentinelConnection().slaves(master);
            System.out.println("============从节点===============");
            for (RedisServer slave : slaves){
                //打印从节点
                System.out.println(slave);
            }
        }
    }
}

实现了通过哨兵的自动读写分离:主节点写,从节点读。

ation.class, args);
}
}

编写测试类:

cn.itcast.demo.RedisTemplateTest

```java
package cn.itcast.demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Collection;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisApplication.class)
public class RedisTemplateTest {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    public void test() {
        //增加测试数据
        redisTemplate.opsForValue().set("username", "XiaoMing");
        //读取数据
        String username = redisTemplate.opsForValue().get("username");
        System.out.println("username:"+username);

        Collection<RedisServer> masters = redisTemplate.getConnectionFactory().getSentinelConnection().masters();
        for (RedisServer master : masters) {
            //打印主节点
            System.out.println("============主节点===============");
            System.out.println(master);

            Collection<RedisServer> slaves = redisTemplate.getConnectionFactory().getSentinelConnection().slaves(master);
            System.out.println("============从节点===============");
            for (RedisServer slave : slaves){
                //打印从节点
                System.out.println(slave);
            }
        }
    }
}

实现了通过哨兵的自动读写分离:主节点写,从节点读。