Redission

发布时间 2023-12-12 20:04:56作者: 吧拉吧拉吧

概述

  1. 简介

    • Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。
      • 基于NIO(即Non-Blocking IO,非阻塞式的输入输出)的Netty框架实现数据的传输。
      • Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
      • Redis具备的功能特性及所起的作用Redisson几乎都涵盖了
  2. 功能特性

    • Redisson虽然只是“客户端”,但它提供了很多“中间件”的功能:
  3. 典型应用场景

    1. “布隆过滤器”(Bloom Filter)

      • 应用场景:“去重业务”(去重指的是判断一个元素是否在一个集合中存在。如果存在,则输出“已存在”的结果;如果不存在,则存储该元素并输出“该元素不存在”的结果。)
      • 布隆过滤器是一个过滤器,用来过滤已经存在的元素,最终保证集合中的元素是唯一的,即所谓的“去重”。
      • 它的作用和JDK自身提供的HashSet作用有异曲同工之妙,只不过它不需要在“判重”之前将数据列表加载至内存中
      • 主要用于“判断一个元素是否存在于大数据量的集合中”。底层逻辑
        • 第一个核心步骤,首先是“初始化”的逻辑,即需要设计并构造K个哈希函数及容量大小为N、每个元素初始取值为0的位数组
        • 第二个核心步骤是“判断元素是否存在”的执行逻辑。首先是需要将当前的元素经过K个哈希函数的计算得到K个哈希值,然后判断K个哈希值(数组的下标)对应数组中的取值是否都为1。如果都为1,则代表该元素是“大概率”性存在的,否则,只要有其中一个取值不为1,则表示元素一定不存在。
      • 优点在于它不需要开辟额外的内存存储元素,从而节省了存储空间
      • 缺点在于判断元素是否在集合中时,有一定的误判率,并且添加进布隆过滤器的元素是无法删除的,因为位数组的下标是多个元素“共享”的,如果删除的话,很有可能出现“误删”的情况
    2. 消息通信

      • 通过Redisson分布式对象中的一个组件“基于发布订阅模式的主题”功能实现
        • “基于订阅-发布模式的主题”的数据组件核心主要由主题、生产者、消费者
          • “主题”,可以理解为消息队列中的消息;
          • 生产者,主要作用在于生产消息,并将消息以主题的形式进行发布;
          • 消费者,主要作用在于订阅主题,并时刻监听是否有相应的主题和消息到来,如果有,则接收并执行相应的业务逻辑。
      • 使用:
        • 首先需要构造“发布-订阅模式的主题”。
        • 当生产者需要发布消息时,只需要将相应的消息或者数据以主题的形式进行发布
        • 而消费者只需要订阅相应的主题即可实现自动监听消费主题和消息,从而执行相应的业务逻辑。
    3. 延迟队列

      • Redisson的延迟队列跟RabbitMQ的死信队列几乎是“双胞胎”,拥有着几乎相同的功能特性,即生产者将消息发送至队列之后,并不会立即被消费者监听消费,而是等待一定的时间TTL,当TTL一到,消息才会被真正的队列监听消费。
      • 阻塞式队列指的是进入到Redisson队列中的消息将会发生阻塞的现象,如果消息设置了过期时间TTL(也叫存活时间),那么消息将会在该阻塞队列“暂时停留”一定的时间,直到过期时间TTL一到,即代表消息该“离开”了,将前往下一个中转站,消息将进入真正的队列,等待着被消费者监听消费。
    4. 分布式锁

      • Redisson的分布式锁可以很好地弥补基于Redis的原子操作实现的分布式锁的缺陷
      • Redisson针对不同场合,设计了不同的分布式锁,主要有:
        • 可重入锁(Reentrant Lock)
          • 当前线程如果没有获取到对共享资源的锁,将会等待一定的时间重新获取。在过个过程中Redisson会提供一个重新获取锁的时间阈值,并在调用tryLock()方法时进行指定。
        • 公平锁(Fair Lock)
        • 联锁(MultiLock)
        • 红锁(RedLock)
        • 读写锁(ReadWriteLock)
        • 信号量(Semaphore)
        • 闭锁(CountDownLatch)

SpringBoot整合Redisson

  • 导入依赖包
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.13.1</version>
    </dependency>
    
  • 在配置文件application.properties配置变量(地址一定要填正确!)
    #redisson配置
    redisson.host.config=redis://127.0.0.1:6379
    
  • 加入Redisson自定义的Bean操作组件RedissonClient
    //导入依赖包
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.env.Environment;
    /**
    * Redisson相关开源组件自定义注入
    * @Author:debug (SteadyJack)
    * @Date: 2019/4/27 13:34
    **/
    @Configuration
    public class RedissonConfig {
      //读取环境变量的实例env
        @Autowired
        private Environment env;
        /**
        * 自定义注入配置操作Redisson的客户端实例
        * @return
        */
        @Bean
        public RedissonClient config(){
            //创建配置实例
            Config config=new Config();
            //可以设置传输模式为EPOLL,也可以设置为NIO等
            //config.setTransportMode(TransportMode.NIO);
            //设置服务节点部署模式:集群模式,单一节点模式,主从模式,哨兵模式等
            //config.useClusterServers().addNodeAddress(env.getProperty("redisson.host.config"),env.getProperty("redisson.host.config"));
            config.useSingleServer()
                    .setAddress(env.getProperty("redisson.host.config"))
                    .setKeepAlive(true);
            //创建并返回操作Redisson的客户端实例
            return Redisson.create(config);
        }
    }
    
  • 测试
    /**单元测试类
    * @Author:debug (SteadyJack)
    **/
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest
    public class RedissonTest {
        //定义日志
        private static final Logger log= LoggerFactory.getLogger(RedissonTest.class);
        //定义操作Redisson的客户端实例
        @Autowired
        private RedissonClient redissonClient;
            //打印输出目前Redisson的配置概况
        @Test
        public void test1() throws Exception{
            log.info("redisson的配置信息:{}",redissonClient.getConfig().toJSON());
        }
    }
    
    

Bloom Filter实战

  • 业务场景“判断一个元素是否存在于一个大数据量的集合中”,测试代码:
    @Test
    public void test() throws Exception{
        //定义Redis中的Key
        final String key="myBloomFilterData";
        //初始化构造数组容量的大小
        Long total=100000L;
        //创建布隆过滤器组件
        RBloomFilter<Integer> bloomFilter=redissonClient.getBloomFilter(key);
        //初始化布隆过滤器,预计统计元素数量为100000,期望误差率为0.01
        bloomFilter.tryInit(total, 0.01);
        //初始化遍历往布隆过滤器添加元素
        for (int i=1;i<=total;i++){
            bloomFilter.add(i);
        }
        //检查特定的元素在布隆过滤器中是否存在并打印输出
        log.info("该布隆过滤器是否包含数据1:{}",bloomFilter.contains(1));
        log.info("该布隆过滤器是否包含数据-1:{}",bloomFilter.contains(-1));
        log.info("该布隆过滤器是否包含数据10000:{}",bloomFilter.contains(10000));
        log.info("该布隆过滤器是否包含数据100000000:{}",bloomFilter.contains(100000000));
    }
    

分析小结

  • 布隆过滤器能用于高效地判断一个元素是否存在于集合中,因此可以将布隆过滤器应用于Redis的缓存击穿问题中。
  • Redisson的延迟队列需要借助“阻塞式队列”作为“中转站”,用于充当消息的第一个暂存区(相当于RabbitMQ的死信交换机)。当TTL即存活时间一到,消息将进入真正的队列被监听消费。
  • RabbitMQ和Redisson的区别
    • RabbitMQ的死信队列依旧保持着FIFO(First In Fist Out,即先进先出)的机制,即先进入死信队列中的消息将先被真正的队列监听消费,而全然不管不同消息携带的不同的TTL的值;
    • Redisson能够弥补RabbitMQ死信队列的这个缺陷,即不管在什么时候,将消息按照TTL从小到大的顺序先后被真正的队列监听、消费。