发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发步者发送消息,订阅者接收消息

使用场景:

公众号、关注系统、网络聊天室、实时广播、实时提醒

特点:

  • Redis发布订阅模式中,发步者可以有多个订阅者,一个订阅者也可以订阅多个发步者

常用命令:

  • psubscribe pattern1 pattern2:订阅一个或多个符合给定模式的频道
  • pubsub subcommand argument1 argument2:查看订阅与发布系统状态
  • pubsub channel message:将消息发送到指定的频道
  • punsubscribe pattern1 pattern2:退订给定模式的频道
  • subscribe channel1 channel2:订阅一个或多个频道的信息
  • unsubscribe channel1 channel2:退订给定的频道

建立实例模型:

  1. 在1号终端进入redis客户端并通过命令:subscribe channel_name 订阅一个频道
  2. 在2号终端进入redis客户端并通过命令:publish channel_name xxx 像目标频道发送一条消息
  3. 在1号终端此时应该可以看到该条消息

原理:

  • 当通过subscribe命令订阅某频道后,redis-server里就会建立一个字典,字典的键就是一个个频道,值则是一个链表,链表中保存了所有订阅该频道的客户端
  • subscribe命令的作用就是将客户端添加到给定channel的订阅链表中
  • 通过publish命令向订阅者发送消息,redis-server会在它维护的字典中查找该channel,并遍历其对应的链表,将消息发送给所有订阅者

Springboot中整合Redis发布订阅

  1. 引入相关依赖

  2. 创建消息监听类

    package com.demo.common;
       
    import org.springframework.stereotype.Component;
       
    @Component
    public class RedisReceiver {
        public void receiveMessage(String message) {
            // TODO 这里是收到通道的消息之后执行的方法
            System.out.println(message);
        }
    }
    
  3. 创建Redis消息订阅配置类

    package com.demo.Redis;
       
    import com.demo.common.RedisReceiver;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.listener.PatternTopic;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
       
    @Configuration
    @EnableCaching
    public class RedisCacheConfig {
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                                MessageListenerAdapter listenerAdapter) {
       
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 可以添加多个 messageListener,配置不同的交换机
            container.addMessageListener(listenerAdapter, new PatternTopic("channel:test"));
       
            return container;
        }
       
        /**
         * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
         * @param receiver
         * @return
         */
        @Bean
        MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
            System.out.println("消息适配器1");
            return new MessageListenerAdapter(receiver, "receiveMessage");
        }
       
        @Bean
        StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
            return new StringRedisTemplate(connectionFactory);
        }
    }
    
  4. 创建接口测试

    Redis订阅发布测试接口

主从复制

指将一台Redis服务器的数据,复制到其他的Redis服务器

前者称为主节点(master/leader),后者称为从节点(slave/follower)

特点:

  • 数据的复制是单向的,只能由主节点复制到从节点
  • 一个主节点可以有多个从节点,一个从节点只能有一个主节点
  • 默认情况下,每台Redis-server都是主节点
  • 主节点一般用于处理写请求,从节点一般用于处理读请求

作用:

  • 主从复制一般用于读写分离的实现,目的是减少单服务器读业务的压力,以达到负载均衡
  • 主从复制实现了数据的热备份,是持久化之外的一种数据沉余方式
  • 当主节点出现故障时,可以由从节点提供服务,以实现故障的快速修复
  • 主从复制是哨兵模式和Redis集群能够实施的基础,即是Redis高可用的基础

建立主从复制模型:

redis-cli执行info replication命令,可以查看该服务主从复制相关配置信息

  1. 创建多个redis.conf配置文件,例如:redis-6379.conf、redis-6380.conf、redis-6381.conf
    • 每个文件配置好port、pidfile、logfile、dbfilename四个属性(不能相同)
    • 如过主服务设置有密码,从服务的conf文件里需要加上:masterauth
  2. 依次启动所有的Redis-server,执行

    linux查询进程

    查看服务启动情况

  3. 分别进入从机cli,执行slaveof host port命令,认定主机

    若要恢复为主机,可以执行命令:slaveof no one

通过命令配置是暂时的!重启后会失效,故一般通过修改配置文件建立主从复制

  1. redis.conf中配置replicaof ;masterauth 即可

须知:

  • 主从模型建立好后,主机写入key,从机就可读取到key(自动增量复制)
  • 一个主机变为从机,立刻会同步其主机的数据(进行一次全量复制)
  • 只有主机可以写key,从机只能读key
  • 主节点宕机,从节点还能进行数据的读取,主节点重启后,主从模型便恢复正常

哨兵模式

在上述的主从模型中,主节点宕机后,需要手动配置修改建立新的主从模型,或者重启主节点,才能恢复整个系统的读写功能,显然这不是一种推荐的方式!

Redis在2.8版本开始提供Sentinel(哨兵)架构来解决这一问题

特点:

  • 能够监控主节点是否故障,若故障,根据投票自动将从节点转为主节点
  • Sentinel是一个独立的进程,会独立运行
  • 主节点宕机被替换后,再次启动,会变为从节点加入系统

原理:

  • Sentinel通过发送命令,等待Redis服务器响应,从而监控运行多个redis-server实例
  • 当哨兵监测到master宕机,会将一个slave切换成master,之后通过发布订阅模式通知其他的从节点,完成配置文件的修改,认到新的主节点

多哨兵模式:

多哨兵模式

  • 一个哨兵进程也可能出现故障,从而使系统存在风险,为此,我们可以使用多个哨兵进行监控,各个哨兵之间还会进行监控,这样就形成了多哨兵模式
  • 若在上图模型中,master宕机,哨兵1先监测到这个结果,系统并不会马上进行failover(故障转移),在其他哨兵也监测到master不可用时(监测到的哨兵达到一定数量),哨兵之间就会进行一次投票,最终投票结果由一个哨兵发出,然后进行failover;最终主节点切换成功后,会通过发布订阅模式,让各个哨兵把自己监控的从节点切换其所认定的主节点
  • 在哨兵1检测到master宕机时,称之为master的主观下线,其他哨兵也检测到并开始投票时,称之为客观下线

哨兵模式的使用:

  1. bin目录建立sentinel.conf文件,进行sentinel的配置

    • sentinel monitor

    • 如:sentinel monitor myredis 127.0.0.1 6379 1

    • 表示当quorum个数的sentinel哨兵监测到master节点故障时,master客观下线,开始投票
    • 若主机有密码,还需配置密码:sentinel auth-pass
  2. 启动哨兵进程:redis-sentinel myconfig/sentinel.conf

上面是简单的单哨兵的使用,多哨兵还需建立不同的配置文件,并分别配置端口等其他配置

缓存穿透

很多系统在把Redis作为缓存数据库来使用时,通常有这样的设定:读的请求先去缓存中查询,若缓存中没有,再去关系型数据中查询

这时若有人频繁查询一个或多个缓存数据库中没有的元素,就会加重关系型数据库的压力,这种情况被称之为缓存穿透

解决方案:

  • 布隆过滤器

    一种数据结构,对所有可能查询的参数以hash形式储存,在控制层先进行校验,不符合则丢弃,从而避免了对底层储存系统的查询压力

  • 缓存空对象

    当存储层不命中后,即使返回空对象,也将其缓存起来,同时设置一个过期时间,之后再访问这个数据将会从缓存中获取,从而保护底层储存系统

缓存击穿

指一个key受到了高并发的访问,如新闻热搜,当这个key在失效的瞬间(过期),持续的大并发就会直接去请求底层储存系统,就像是在屏障上凿开了一个洞

解决方案:

  • 设置热点数据永不过期

  • 加分布式锁(在底层储存系统)

    使用锁机制,保证对于每个key,在同一时间只有一个线程去访问,其他线程由于没有获得分布式锁的权限,从而进行等待;

    这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大

缓存雪崩

指某一时间段,缓存集中过期失效;如Redis宕机,或大量数据集中过期,而对于这些数据的访问,就会一时间落在底层上,对其产生周期性的压力

解决方案:

  • Redis集群的搭建,异地多活

  • 流量降级

    暂时停掉一些不重要的服务;分布式锁

  • 数据预热

    在发生大并发访问之前,手动缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀