分布式框架-Redis 04 哨兵HA架构

840 阅读4分钟

1 概述

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

2 Redis哨兵架构

哨兵架构下客户端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给客户端(这里的redis的客户端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)

2.1 哨兵架构搭建

  1. 复制一份sentinel.conf文件
cp sentinel.conf sentinel_26379.cof
  1. 将相关配置修改为如下:
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‐redis‐name> <master‐redis‐ip> <master‐redis‐port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 192.168.0.60 6379 2 # mymaster这个名字随便取,客户端访问时会用
  1. 启动sentinel哨兵实例
src/redis-sentinel sentinel_26379.conf
  1. 查看sentinel的info信息
redis-cli -p 26379
127.0.0.1:26379>info

可以看到sentinel的info里面已经识别出了redis的主从

  1. 可以再多配置2台sentinel

  2. 集群都启动完成后,会将哨兵集群的元数据信息写入所有sentinel的配置文件(追加在文件的最后):

sentinel known‐replica mymaster 192.168.0.60 6380 #代表redis主节点的从节点信息
sentinel known‐replica mymaster 192.168.0.60 6381 #代表redis主节点的从节点信息
sentinel known‐sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c569
35760f #代表感知到的其它哨兵节点
sentinel known‐sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8
bd5ca6 #代表感知到的其它哨兵节点

当redis主节点如果挂了,哨兵集群会重新选举出新的redis主节点,同时会修改所有sentinel节点配置文件 的集群元数据信息,比如6379的redis如果挂了,假设选举出的新主节点是6380,则sentinel文件里的集 群元数据信息会变成如下所示:

sentinel known‐replica mymaster 192.168.0.60 6379 #代表主节点的从节点信息
sentinel known‐replica mymaster 192.168.0.60 6381 #代表主节点的从节点信息
sentinel known‐sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c569
35760f #代表感知到的其它哨兵节点
sentinel known‐sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8
bd5ca6 #代表感知到的其它哨兵节点

同时还会修改sentinel文件里之前配置的mymaster对应的6379端口,改为6380

sentinel monitor mymaster 192.168.0.60 6380 2

当6379的redis实例再次启动时,哨兵集群根据集群元数据信息就可以将6379端口的redis节点作为从节点 加入集群

2.2 redis哨兵示例

基本演示:

public class JedisSentinelTest {

    public static void main(String[] args) throws IOException {

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);

        String masterName = "mymaster";
        Set<String> sentinels = new HashSet<String>();
        sentinels.add(new HostAndPort("192.168.0.60", 26379).toString());
        sentinels.add(new HostAndPort("192.168.0.60", 26380).toString());
        sentinels.add(new HostAndPort("192.168.0.60", 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", "zhuge"));
            System.out.println(jedis.get("sentinel"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
            if (jedis != null)
                jedis.close();
        }

    }
}

结合springboot: 加入依赖:

<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>

核心配置:

server:
  port: 9090

spring:
  application:
    name: redis-demo
  redis:
    database: 0
    timeout: 3000
    sentinel:
      master: mymaster
      nodes:
        - 192.168.0.60:26379
        - 192.168.0.60:26380
        - 192.168.0.60:26381
    lettuce:
      pool:
        max-idle: 50
        min-idle: 10
        max-active: 100
        max-wait: 1000

相关代码:

@RestController
public class JedisSentinelIndex {

    @Autowired
    private 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("zhuge" + i, i + "");
                System.out.println("设置key:" + "zhuge" + i);
                i++;
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
}