一文搞懂Redis Sentinel
其实redis真正的高可用只有两种:Redis Sentinel(哨兵模式)
Redis Cluster(集群默认)
如果有人说还有主从复制模式,那你就可以用这篇文章甩出来怕怕打他的脸了。主从复制就两台机器,如果master挂了还得手动切换,这样算高可用?顶多算是备份。下面我们来详细说一下这两种高可用的配置方式以及使用方法。
Redis哨兵模式(Sentinel)
Sentinel的特点是自动故障转移,单节点运行。为什么我这里说单节点运行呢?是因为在Sentinel模式下,真正给应用提供服务的只有master一台机器,它使用的内存容量是有限的,不能像集群那也成倍增长,但是他最大的优点就是他的自动故障转移。应用服务器只需要连接Sentinel节点就好了,不需要关心到底有几台在运行的redis节点,因为Sentinel会给应用程序返回可用的master节点。我们采用先6个redis节点来搭建Redis Sentinel
服务器类型 | 角色 | ip | 端口 |
---|
redis | master | 127.0.0.1 | 6379 |
| slave | 127.0.0.1 | 6380 |
| slave | 127.0.0.1 | 6381 |
sentinel | - | 127.0.0.1 | 26379 |
| - | 127.0.0.1 | 26380 |
| - | 127.0.0.1 | 26381 |
配置文件
我们在redis的安装目录下新建redis-sentinel目录:
- 复制redis.conf到redis-sentinel目录中,并修改文件名,格式:redis-{role}-{port}.conf
- 复制sentinel.conf到redis-sentinel目录中,并修改文件名,格式:sentinel-{port}.conf
最终如下图
部署架构
最终的部署效果是,sentinel节点相互监控,每个sentinel都监控redis的master节点,并通过master节点知晓它的所有slave节点
redis节点的配置
redis-master-6379.conf 修改内容
# 任意网络可访问(注意此参数在生产环境不可以配置的这么随意)
bind 0.0.0.0
# 设置master节点的访问密码
requirepass 123456
redis-slave-6380.conf、redis-slave-6381.conf 配置修改
# 任意网络可访问(注意此参数在生产环境不可以配置的这么随意)
bind 0.0.0.0
# 设置slave节点的访问密码
requirepass 123456
# master节点的ip端口
slaveof 127.0.0.1 6379
# master节点的密码
masterauth 123456
# 修改为自己的端口号
port 6381
sentinel节点的配置
sentinel-26379.conf
# 配置sentinel的监听,参数解析
# sentinel monitor
# :master的名称
# :master节点的ip
# :master节点的端口
# :代表大于 个sentinel个节点认为master不可用时,才会进行故障转移
sentinel monitor mymaster 127.0.0.1 6379 2
# master节点的密码
sentinel auth-pass mymaster 123456
# 修改为自己的端口
port 26379
启动命令
完成以上操作后,就可以到命令行启动了。redis Sentinel的启动顺序是 master->slave->slave->sentinel,所以我们的启动命令就是
# 进入redis bin目录
cd redis4.0.11/bin/
# 启动redis
./redis-server ../redis-sentinel/redis-master-6379.conf &
./redis-server ../redis-sentinel/redis-slave-6380.conf &
./redis-server ../redis-sentinel/redis-slave-6381.conf &
# 启动sentinel
./redis-server ../redis-sentinel/sentinel-26379.conf --sentinel &
./redis-server ../redis-sentinel/sentinel-26380.conf --sentinel &
./redis-server ../redis-sentinel/sentinel-26381.conf --sentinel &
启动成功的验证
sys@syl bin % ps -ef|grep redis
501 77809 12304 0 5:54下午 ttys001 0:00.07 ./redis-server 0.0.0.0:6379
501 77810 12304 0 5:54下午 ttys001 0:00.06 ./redis-server 0.0.0.0:6380
501 77811 12304 0 5:54下午 ttys001 0:00.06 ./redis-server 0.0.0.0:6381
501 78130 12304 0 5:55下午 ttys001 0:00.06 ./redis-server *:26379 [sentinel]
501 78351 12304 0 5:56下午 ttys001 0:00.01 ./redis-server *:26380 [sentinel]
501 78352 12304 0 5:56下午 ttys001 0:00.01 ./redis-server *:26381 [sentinel]
501 78376 12304 0 5:56下午 ttys001 0:00.00 grep redis
看到这里的胖友可能有些疑惑,sentinel的配置文件只是配置了master节点,其他sentinel节点、slave节点的信息都没有配置,他们是怎么感知到的呢?这个一个好问题,sentinel是通过发布订阅功能来自动发现正在监视相同master节点的其他sentinel的(也就是通过发布订阅功能发现了自己的兄弟节点)。那第二个问题,sentinel是如何自动发现slave的呢?事实上sentinel只需要知道master节点就可以了,因为master节点上记录了它对于的slave节点的数据,所以sentinel获取到master节点的时候,就可以问master要注册在它下面的slave节点的信息了。这一点很有用,在redis cluster中也会用到这一点。
几个重要参数说明
还有几个sentinel配置的重要的参数上面没有给出,下面整理一下统一说明
sentinel down-after-milliseconds <服务名称> <毫秒数>
指定哨兵在监控Redis服务时(sentinel会定时向redis服务发送PING命令),当Redis服务在一个默认毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30000(30秒)
sentinel parallel-syncs <服务名称> <服务器数>
指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求越高
sentinel failover-timeout <服务名称> <毫秒数>
故障切换的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟
sentinel notification-script <服务名称> <脚本路径>
指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,比较常用
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
sentinel client-reconfig-script
当发生故障转移时,可以调用脚本来执行特定的任务。
主观下线和客观下线的定义
Redis 的 Sentinel 中关于下线有两个不同的概念:主观下线:指的是单个Sentinel对redis服务器做出了下线判断
客观下线:指的是多个Sentinel实例在对redis同一个服务做出了Sdown的判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。
java程序如何访问Sentinel集群
pom.xml引入
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.1.0version>
dependency>
java代码
public class Main {
public static String USER_UV = "user_uv";
public static void main(String[] args) {
HashSet sentinelList = new HashSet<>(Arrays.asList("127.0.0.1:26379",
"127.0.0.1:26380","127.0.0.1:26381"));
JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster",sentinelList,"123456");
Jedis jedis = sentinelPool.getResource();
jedis.set(USER_UV,"123123123");
String s = jedis.get(USER_UV);
System.out.println(s);
}
}