我们前面学习了Redis的主从模式,可以实现读写分离和数据备份,减轻Redis中master节点的压力。但是主从模式仅仅是减轻了master节点的读压力和做数据备份,一旦master节点挂了之后,我们只能手动的去修改,让其中一个从节点成为新的主节点(修改配置中的ip),让其他的从节点再去同步新主节点的数据。

很显然,对于这种需要运维人员去手动修改的操作我们在要求高可用的情况下远远是不够的,不能接受的。

所以,我们需要使用新的哨兵模式,来支持redis的主节点挂掉之后,会从从节点之中自动的重新选举出一个主节点的功能。

一、Redis哨兵高可用架构


sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis的主节点(client不是一开始就连接redis服务,而是先连接哨兵服务,从哨兵服务上获取到redis服务的信息后,之后直接访问redis服务),后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点。

当redis的主节点发生变化( master节点如果挂掉之后,哨兵会选举出新的master节点),哨兵会第一时间感知到,并且 将新的redis主节点通知给client端(这样就可以实现高可用了,即一个master节点挂了之后,哨兵会自动选举出另一个新的master节点,并将新master节点的信息发送给客户端)(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)。

深入思考:

我们客户端连接哨兵模式的redis的时候,连接地址自然是要写哨兵的,肯定不能写某个redis服务的IP+端口。为什么?

一旦我们的redis服务的主节点挂掉之后,如果我们采用的是直接写redis服务的IP+端口的模式,根本不可能成功访问,因为这个服务已经挂掉了。

所以说我们要找一个中介点,让其管理redis服务的连接信息,而我们客户端只需要连接到这个中介点上,每次都动态的去这个中介点上去拉取redis的主节点的连接信息就好了。

这样,即使redis服务的主节点挂了,redis服务会把新选举出来的主节点的信息更新到这个中介点上,我们服务端只需要去这个中介点上再拉取一次,就可以获取到新的主节点的信息了,而此时我们对于这个中介点的配置是没有改变的,所以这样就实现了Redis的高可用。

这种思想与使用KeepAlive + HAproxy是一模一样的,IP地址不能写真实服务地址,而是写一个中间服务的地址,然后由这个中间服务去拉取真实的服务地址。

二、redis哨兵架构搭建步骤

Redis daemonize介绍

1、daemonize介绍

  • A、redis.conf配置文件中daemonize守护线程,默认是NO。
  • B、daemonize是用来指定redis是否要用守护线程的方式启动

2、daemonize 设置yes或者no区别

  • daemonize:yes:redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
  • daemonize:no:
    当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭连接工具(putty,xshell等)都会导致redis进程退出。

配置哨兵模式

注意,Redis中已经包含了哨兵模式,有一个sentinel.conf文件,我们用这个文件就可以启动哨兵服务了。

注意,redis中有个bind配置:

对于Redis中bind的正确的理解是:
bind:是绑定本机的IP地址,(准确的是:本机的网卡对应的IP地址,每一个网卡都有一个IP地址),而不是redis允许来自其他计算机的IP地址。

1、复制一份sentinel.conf文件

cp sentinel.conf sentinel‐26379.conf

2、打开sentinel‐26379.conf配置文件,将相关配置修改为如下值:

port 26379
daemonize yes
pidfile "/var/run/redis‐sentinel‐26379.pid"
logfile "26379.log"
dir "/usr/local/redis/redis-6.2.3/data/26379"# sentinel monitor <master‐redis‐name> <master‐redis‐ip> <master‐redis‐port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 192.168.131.171 6379 2 # mymaster这个名字随便取,客户端访问时会用到

这里的sentinel总数配置成了2,表示我们现在创建的3个哨兵服务中,至少有两个哨兵认为redis的master节点挂了,才会开始选举新的master节点。

注意,monitor 的IP地址不能写成127.0.0.1,必须写成真实的IP地址!否则通过Jedis访问会报错。因为哨兵会根据自己配置中设置的"sentinel monitor mymaster 192.168.131.171 6380 2"这个IP和端口去连接Redis的master节点。如果我们在sentinel.conf中写成127.0.0.1, 就回去连接本机的端口,就不可能会连接到真实的redis的master服务节点上。并且reids服务中bind 也不能写成127.0.0.1,就会导致Jedis无法连接。

3、启动sentinel哨兵实例

src/redis‐sentinel sentinel‐26379.conf


4、查看sentinel的info信息

[root@localhost redis-6.2.3]# ./src/redis-cli -p 26379
127.0.0.1:26379> info
# Server
redis_version:6.2.3
redis_git_sha1:00000000


5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改

常见问题-只显示一个哨兵

注意:哨兵配置复制时,sentinel.conf 中的myid不能相同 ,要删掉myid,删掉重新重启时会自动分配,否则只会显示只有一个哨兵。

配置多个哨兵:验证是否配置成功

我们此时开启Redis的master节点6379和两个从节点6380、6381;然后再开启三个哨兵26379、26380、26381:

然后我们可以登录到26379这个哨兵客户端下,验证配置是否成功:

[root@localhost redis-6.2.3]# ./src/redis-cli -p 26379
127.0.0.1:26379> info
...........
...........
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3

我们可以看到从节点有两个,有三个哨兵!说明配置成功。

sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:

sentinel known-replica mymaster 127.0.0.1 6381  #代表redis主节点的从节点信息
sentinel known-replica mymaster 127.0.0.1 6380  #代表redis主节点的从节点信息
sentinel known-sentinel mymaster 127.0.0.1 26381 76e5aa124b737a9d103d0e0fc4cbaf681c94f33f  #代表感知到的其它哨兵节点,注意,自己配置哨兵的时候如果复制了sentinel.conf文件,一定要查看其中的mid是不是相同,如果相同要删除掉重启,会自动生成,否则可能只会显示一个哨兵
sentinel known-sentinel mymaster 127.0.0.1 26380 e305c5d34f59f3940dc27134927fefb89cc7d18f  #代表感知到的其它哨兵节点
[root@localhost redis-6.2.3]#

测试Redis的master节点挂掉

我们此时来关闭6379这个redis服务:

[root@localhost redis-6.2.3]# ps -ef | grep redis
root       9701      1  0 11:23 ?        00:00:09 ./src/redis-server 127.0.0.1:6379
root       9709      1  0 11:23 ?        00:00:09 ./src/redis-server *:6380
root       9790   9168  0 11:43 pts/5    00:00:00 vim redis-6380.conf
root       9795      1  0 11:45 ?        00:00:06 ./src/redis-server *:6381
root       9802   9723  0 11:45 pts/0    00:00:00 ./src/redis-cli -p 6381
root       9954      1  0 12:09 ?        00:00:03 ./src/redis-sentinel *:26381 [sentinel]
root       9960      1  0 12:09 ?        00:00:03 ./src/redis-sentinel *:26380 [sentinel]
root       9966      1  0 12:09 ?        00:00:03 ./src/redis-sentinel *:26379 [sentinel]
root       9975   9189  0 12:16 pts/6    00:00:00 ./src/redis-cli -p 26379
root       9978   9147  0 12:19 pts/4    00:00:00 grep --color=auto redis
[root@localhost redis-6.2.3]# kill -9 9701

一段时间后我们再来查看信息:

# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.131.171:6380,slaves=2,sentinels=3
127.0.0.1:26379> 

我们可以看到redis的master节点已经换成了6380. 即哨兵把6380这个服务选举为了新的主节点。

三、使用Jedis访问哨兵

依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>

测试代码

package com.jihu.jedis.master_slave;import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;import java.io.IOException;
import java.util.HashSet;
import java.util.Set;public class JedisSentinelTest {public static void main(String[] args) throws IOException {JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(20);config.setMaxIdle(10);config.setMinIdle(5);// 这个masterName 是配置在sentinel.conf文件中的String masterName = "mymaster";Set<String> sentinels = new HashSet<String>();sentinels.add(new HostAndPort("192.168.131.171",26379).toString());sentinels.add(new HostAndPort("192.168.131.171",26380).toString());sentinels.add(new HostAndPort("192.168.131.171",26381).toString());//JedisSentinelPool其实本质跟JedisPool类似,都是与redis主节点建立的连接池//JedisSentinelPool并不是说与sentinel建立的连接池,而是通过sentinel发现redis主节点并与其建立连接JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, config, 3000, null);Jedis jedis = null;try {jedis = jedisSentinelPool.getResource();System.out.println(jedis.set("sentinel", "xiaoyan"));System.out.println(jedis.get("sentinel"));} catch (Exception e) {e.printStackTrace();} finally {//注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。if (jedis != null)jedis.close();}}
}
六月 29, 2021 6:19:12 下午 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Trying to find master from available Sentinels...
六月 29, 2021 6:19:12 下午 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Redis master running at 192.168.131.171:6380, starting Sentinel listeners...
六月 29, 2021 6:19:13 下午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.131.171:6380
OK
xiaoyanProcess finished with exit code 0

四、使用SpringData访问哨兵

1、引入相关依赖:

<?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"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.0.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><groupId>com.jihu</groupId><artifactId>springboot-jedis</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency></dependencies></project>

2、配置文件

server:port: 8090spring:redis:database: 0timeout: 3000sentinel:  # 哨兵模式master: mymaster #主服务器所在集群名称nodes: 192.168.131.171:26379, 192.168.131.171:26381, 192.168.131.171:26381lettuce:pool:max-idle: 50min-idle: 10max-active: 100max-wait: 1000

3、测试代码

@SpringBootApplication
public class RedisApplication {public static void main(String[] args) {SpringApplication.run(RedisApplication.class, args);}
}
package com.jihu.redis.controller;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class IndexController {private static final Logger logger = LoggerFactory.getLogger(IndexController.class);@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到* 新的master选举出来后,哨兵会把消息发布出去,客户端实际上是实现了一个消息监听机制,* 当哨兵把新master的消息发布出去,客户端会立马感知到新master的信息,从而动态切换访问的masterip** @throws InterruptedException*/@RequestMapping("/test_sentinel")public void testSentinel() throws InterruptedException {int i = 1;while (true){try {stringRedisTemplate.opsForValue().set("xiaoyan"+i, i+"");System.out.println("设置key:"+ "xiaoyan" + i);i++;Thread.sleep(1000);}catch (Exception e){logger.error("错误:", e);}}}
}

我们启动上面的springboot项目,然后调用http://localhost:8090/test_sentinel,会不断的向redis中写入数据。

然后我们将其redis目前的master节点6380关掉,看看程序是否还可以继续执行下去。

我们kill掉redis的master节点服务后:

.........
设置key:xiaoyan46
设置key:xiaoyan47
2021-06-29 19:10:52.121  INFO 22352 --- [xecutorLoop-1-1] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was /192.168.131.171:6380
2021-06-29 19:10:54.149  WARN 22352 --- [ioEventLoop-4-4] i.l.core.protocol.ConnectionWatchdog     : Cannot reconnect to [192.168.131.171:6380]: Connection refused: no further information: /192.168.131.171:6380
2021-06-29 19:10:55.456 ERROR 22352 --- [nio-8090-exec-1] c.jihu.redis.controller.IndexController  : 错误:org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:70) ~[spring-data-redis-2.3.0.RELEASE.jar:2.3.0.RELEASE]at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41) ~[spring-data-redis-2.3.0.RELEASE.jar:2.3.0.RELEASE]at ...............................
Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:120) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at com.sun.proxy.$Proxy65.set(Unknown Source) ~[na:na]at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.set(LettuceStringCommands.java:146) ~[spring-data-redis-2.3.0.RELEASE.jar:2.3.0.RELEASE]... 59 common frames omitted2021-06-29 19:10:58.409  INFO 22352 --- [xecutorLoop-1-1] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was 192.168.131.171:6380
2021-06-29 19:10:58.464 ERROR 22352 --- [nio-8090-exec-1] c.jihu.redis.controller.IndexController  : 错误:

随后的一段时间,Redis服务仍然处于不可用状态,因为调用一直报错,这是因为哨兵在内部选举新的master节点。

又过了几秒钟后:

.........
.........
Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:120) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80) ~[lettuce-core-5.3.0.RELEASE.jar:5.3.0.RELEASE]at com.sun.proxy.$Proxy65.set(Unknown Source) ~[na:na]at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.set(LettuceStringCommands.java:146) ~[spring-data-redis-2.3.0.RELEASE.jar:2.3.0.RELEASE]... 59 common frames omitted2021-06-29 19:11:25.209  INFO 22352 --- [xecutorLoop-1-4] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was 192.168.131.171:6380
2021-06-29 19:11:25.218  INFO 22352 --- [ioEventLoop-4-4] i.l.core.protocol.ReconnectionHandler    : Reconnected to 127.0.0.1:6379
设置key:xiaoyan48
设置key:xiaoyan49
设置key:xiaoyan50
.......

然后Redis服务又可以正常使用了。而且命令是接着上次的继续往下执行。

我们此时再来看一下Redis的主从和哨兵信息:


可以看到,此时的Redis主结点已经变成了6379服务。

五、StringRedisTemplate与RedisTemplate详解

spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在RedisTemplate中提供了几个常用的接口方法的使用,分别是:

private ValueOperations<K, V> valueOps;
private HashOperations<K, V> hashOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;

RedisTemplate中定义了对5种数据结构操作:

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。

StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。

RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

Redis客户端命令对应的RedisTemplate中的方法列表:


Redis高可用之哨兵模式相关推荐

  1. Redis高可用方案-哨兵模式-SpringBoot整合

    哨兵是用来放哨的,能实时监控我们redis集群的状态,保证redis服务器不会挂掉 ​ 搭建:https://blog.csdn.net/Zer01ne/article/details/8301040 ...

  2. redis高可用(哨兵模式篇)

    哨兵模式(sentinel) ​ 当我们使用主从复制时,从库宕机依然可以将请求发送给主库或者其他从库,但是 Master 宕机,只能响应读操作,写请求无法再执行.所以主从复制架构面临一个严峻问题,主库 ...

  3. Redis 高可用之哨兵集群

    Redis 高可用之哨兵集群 前言 哨兵集群 启动并初始化 Sentinel 初始化 Sentinel 状态 初始化Sentinel监视的主服务器列表 创建连向主服务器的网络连接 获取主服务器信息 获 ...

  4. Redis高可用方案-哨兵与集群

    祝大家每日进步,有技术问题多多交流,同时欢迎大家关注我的头条号:IT人孙会良 Redis高可用方案 一.名词解释 二.主从复制 Redis主从复制模式可以将主节点的数据同步给从节点,从而保障当主节点不 ...

  5. Redis高可用方案哨兵机制------ 配置文件sentinel.conf详解

    Redis的哨兵机制是官方推荐的一种高可用(HA)方案,我们在使用Redis的主从结构时,如果主节点挂掉,这时是不能自动进行主备切换和通知客户端主节点下线的. Redis-Sentinel机制主要用三 ...

  6. Redis高可用之集群配置(六)

    0.Redis目录结构 1)Redis介绍及部署在CentOS7上(一) 2)Redis指令与数据结构(二) 3)Redis客户端连接以及持久化数据(三) 4)Redis高可用之主从复制实践(四) 5 ...

  7. redis 高可用(持久化、主从复制、哨兵、集群)以及集群的三种模式

    Redis高可用定义 在web服务器中,高可用代表服务器可以正常访问的时间,一般使用百分比来衡量多长时间内可以提供正常服务 但是在redis中,高可用的定义还要更广泛一点,除了提供正常的服务(如主从分 ...

  8. 2.redis高可用-持久化-主从复制-哨兵-cluster集群概述与部署,内容依旧多看完直接通透!

    文章目录 一,Redis 高可用 1.持久化 2.主从复制 3.哨兵 4.集群(cluster) 二,Redis 持久化方式 1.持久化的功能 2.持久化的方式 三, RDB 持久化 1.触发条件 2 ...

  9. 【❤️万字长文总结❤️】一篇学会Redis高可用✔集群✔搭建详细教程

    大家好,我是Lex 喜欢欺负超人那个Lex 擅长领域:python开发.网络安全渗透.Windows域控Exchange架构 今日重点:今天总结一下Redis集群高可用的搭建流程 [惊喜推荐+优质资源 ...

最新文章

  1. iis上实现虚拟目录
  2. win7 修改hosts 不起作用
  3. SQL Server 堆表行存储大小(Record Size)
  4. boost::describe模块实现打印功能的测试程序
  5. BayaiM__SQLLDR_linux_shell高级版
  6. 【BZOJ2151】种树,贪心+Splay乱搞
  7. Ubuntu install mysql
  8. bzoj2151: 种树
  9. XP框架开启debug模式_推荐一个兼容性强完美支持XP框架的安卓模拟器,一直在用!...
  10. 将自己的图片做成cityscape格式(自用)
  11. Android APP漏洞自动化静态扫描检测工具-Qark
  12. Python 复数属性和方法操作实例
  13. 惠普暗影精灵u盘启动linux,暗影精灵5 安装w10+ Ubuntu18.0.4
  14. js中[object,object]是什么,怎么取值
  15. HDU 3629-Convex找凸四边形个数(扫描+二分/two pointers)
  16. mysql修改元宝,端游[君·天下]高仿魔兽世界一键启动服务端+配套客户端+元宝金币修改教程等...
  17. 2021-2022学年广州市广大附中九年级第二学期开学考试英语试题
  18. jython 引入java.lang_Jython与Java的互相调用
  19. 安卓端,手机端如何运行exe.文件
  20. 读书笔记:《乌合之众》--群体时代的大众心理

热门文章

  1. 02-PS工具栏介绍
  2. javascript动画制作
  3. Google操作系统基于Chrome?!
  4. 【mba项目管理论文】S 公司项目管理绩效评价现状与问题(节选)
  5. 如何修改单元格的宽度html,如何在HTML中设置单元格的宽度和高度?
  6. 网络营销实战课-笔记2
  7. eclipse安装EMF插件
  8. 武汉大学计算机学院学号代码,武汉大学各专业代码
  9. 【大屏项目】SpringBoot + Vue 实现的可视化拖拽编辑的
  10. 小程序 横向滚动导航栏