主从搭建是后面哨兵和集群的基础,虽然生产环境很少使用。
我们就以一主两从来搭建。我这里就用一台机器来搭建了。
1:复制一份redis.conf配置文件命名为redis-6380.conf
2:修改配置文件中的如下配置:
- port 6380 #server占用的端口号,保持唯一,避免冲突
-
- pidfile /var/run/redis_6380.pid #把pid进程号写入pidfile配置的文件
-
- logfile "6380.log" #日志文件
-
- dir ./data/6380 #指定数据的目录
-
- #bind 127.0.0.1 注释掉
-
-
- replicaof 127.0.0.1 6379 #从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
- replica-read-only yes #配置从节点只读
同上,复制一份6381的配置文件,注意,dir目录要提前创建出来,不然会报错,如果启动成功,可以通过ps -ef | grep redis查看到三个实例了。
用-p 端口号可以连上这三个server.
src/redis-cli -p port #6379-6381
其中6379是主服务,用来写命令操作,写入之后在从服务6380,6381客户端都可以查询到。
1:如果为master配置了一个slave,无论这个slvae是否是第一次连接上master,它都会发送一个PSYNC命令给master请求复制数据。
2:master收到PSYNC命令后,会通过bgsave生成当时的rdb快照,master会继续接收客户端发送的命令请求,它会把这个期间可能修改数据集的请求缓存在内存中。当rdb快照生成之后会发送slave实例,slave用这个rdb快照放到自己的数据目录下并加载到内存中。
3:master生成快照发送给slave期间缓存在内存中的命令,会再次发送给slave。
4:当master和slave因为某些原因断开的时候,slave能够自动重连Master,Master如果并发收到多个slave连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
当master和slave断开重连之后,一般都会对整份数据进行复制。但是从redis2.8版本开始,redis改用可以支持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。
master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标offset太旧了,已经不在master的缓存队列里面了,那么将会进行一次全量的数据复制。
如果有很多从节点,为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如下架构,让部分从节点与从节点同步数据。
sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。哨兵模式架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)
哨兵架构搭建步骤:
从上面架构图可以看出,哨兵集群只要主从集群的的基础上搭建一个哨兵集群就可以了。
哨兵实例使用的是sentinel.conf
1: 复制一份sentinel.conf 为sentinel-26379.conf
3:修改如下配置
- port 26379 #哨兵实例也有端口号
-
- daemonize yes
-
- pidfile "/var/run/redis‐sentinel‐26379.pid"
-
- logfile "26379.log"
-
- dir "/usr/local/redis‐5.0.3/data"
-
- #sentinel monitor <master-name> <ip> <redis-port> <quorum>
- # quorum是一个数字,指明当有多少个sentinel认为一个master失效时,
- # 一般是seentinel实例总数/2 +1
-
- #这里打算起三个sentinel实例 ,mymaster只是一个集群名字,可以自定义
- sentinel monitor mymaster 127.0.0.1 6379 2
-
4:启动哨兵实例
src/redis-sentinel ./conf/sentinel-26270.conf
按照上面的方式再启动两个哨兵实例
启动完成,有三个redis服务实例,三个sentinel实例。
使用:src/redis-cli -p 6380 进入到6380的这个redis客户端,执行info命令
展示的信息里面有一项Replication,
可以看到当前这个实例的角色是slave,主节点是6379端口的实例。
src/redis-cli -p 26380 去看下哨兵实例的信息
在最后一行显示哨兵的实例数只有一个,并没有其它哨兵节点的信息。说明哨兵之间并没有发生通信,这样就不会发生选举事情。
至于为什么出现这样的情况,和哨兵使用的配置文件有关,这边启动的三个哨兵实例都是直接复制文件来的,里面有个关键配置:sentinel myid 来标识不同哨兵实例,三个实例是同一个值,这里把它都注释掉重新启动哨兵实例,让其自动生成。
这样启动之后,在任一个哨兵配置文件的最后可以看到其它哨兵的信息:
引入依赖:
- <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>
相关yml配置:
-
- spring:
- redis:
- database: 0
- timeout: 3000 #read time out
- connect-timeout: 3000 #connection time out
- sentinel:
- master: mymaster #哨兵conf中自定义的
- nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381 #哨兵节点
- lettuce:
- pool: #连接池
- max-active: 100
- max-idle: 50
- min-idle: 10
- max-wait: 1000
- server:
- port: 8980
- @GetMapping("/test")
- public void testRedis() throws InterruptedException {
- int i = 0;
- while (true) {
- try{
- stringRedisTemplate.opsForValue().set("key:" + i, i + "", 100, TimeUnit.SECONDS);
- i++;
- }catch (Exception ex){
- log.info("出现了异常: cause: {}",ex.getCause());
- }
- Thread.sleep(4000L);
- }
- }
刚开始的时候,哨兵和主从集群是正常的,可以在redis实例的客户端都能看到放入的数据,当我们把redis的Master节点Kill掉之后,程序就会报错,因为sentinel实例选举新的master节点需要时间,这段时间本地还是尝试连接之前的master节点,所以会连接失败。
- 2022-06-30 22:20:30.401 WARN 49016 --- [oEventLoop-4-10] i.l.core.protocol.ConnectionWatchdog : Cannot reconnect to [127.0.0.1:6379]: Connection refused: no further information: /127.0.0.1:6379
- 2022-06-30 22:20:31.149 INFO 49016 --- [nio-8980-exec-1] c.z.m.demo.controller.RedisController : 出现了异常: cause: {}
-
- io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)
等到sentinel选举出来新的master节点之后,会重新连接上:Reconnected to 127.0.0.1:6380
- 2022-06-30 22:20:34.578 INFO 49016 --- [xecutorLoop-1-7] i.l.core.protocol.ConnectionWatchdog : Reconnecting, last destination was 127.0.0.1:6379
- 2022-06-30 22:20:34.727 INFO 49016 --- [oEventLoop-4-12] i.l.core.protocol.ReconnectionHandler : Reconnected to 127.0.0.1:6380