SpringBoot 的 RedisTemplate、Redisson

news2025/1/23 17:33:50

一、Jedis、Lettuce、Redisson的简介

优先使用Lettuce,
需要分布式锁,分布式集合等分布式的高级特性,添加Redisson结合使用。
对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害。

1.1、Jedis

老牌Redis的Java客户端,提供比较全面的Redis命令的支持,
使用阻塞的I/O,方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。
Jedis客户端实例不是线程安全的,使直接连接redis server,需要通过连接池来使用Jedis,为每个jedis实例增加物理连接。

1.2、Lettuce

   SpringBoot2之后,默认就采用了lettuce。 
高级Redis客户端,基于Netty框架的事件驱动的通信层,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
Lettuce的API是线程安全的,可以操作单个Lettuce连接来完成各种操作,连接实例(StatefulRedisConnection)可在多个线程间并发访问。

1.3、Redisson

基于Netty框架的事件驱动的通信层,方法是异步的,API线程安全,可操作单个Redisson连接来完成各种操作。
实现了分布式和可扩展的Java数据结构,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。
提供很多分布式相关操作服务,如,分布式锁,分布式集合,可通过 Redis支持延迟队列。

二、SpringBoot 的 RedisTemplate

2.1、配置

<!--redis(spring-boot-starter-data-redis中包含的Lettuce)-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Lettuce使用线程池必要包-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!-- Redisson依赖 -->
<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.23.2</version>
</dependency>  
####################redis连接配置############
redis:
#   cluster:
#	nodes:
#	  - 127.0.0.1:7001
#	  - 127.0.0.1:7002
#	  - 127.0.0.1:7003
#	   host: 127.0.0.1
  port: 6379
  password: 123456
  database: 0
  timeout: 2000ms
  lettuce:
    pool:
      # 连接池最大连接数
      max-active: 20
      # 连接池中的最小空闲连接
      max-idle: 10
      # 连接池最大阻塞等待时间(使用负数表示没有限制,单位ms)
      max-wait: 3000

2.2、代码使用

2..2.1.配置 RedisTemplate

@Configuration
public class RedisConfig {
	/**
     * 创建 RedisTemplate,注入IOC容器
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置数据源的连接工厂(默认会传入框架中自带的(也就是读取完配置文件装配的)LettuceConnectionFactory)
        // 也可以自己定义,注入容器,再通过@Qualifier("")传进来 
        template.setConnectionFactory(factory);
        //设置key的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        return template;
    }
}

2..2.3.RedisTemplate 封装

@Component
public class RedisClient {
 
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
 
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     */
    public boolean expire(String key,long time){
        try {
            if(time>0){
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long ttl(String key){
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);
    }
 
    //============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key){
        return key==null?null:redisTemplate.opsForValue().get(key);
    }
 
    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key,Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    public Boolean del(String key){
       return redisTemplate.delete(key);
    }
 
    //================================hash=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     */
    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(key, item);
    }
 
    /**
     * 向 hash 表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item){
        redisTemplate.opsForHash().delete(key,item);
    }
 
    //============================set=============================
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     */
    public Set<Object> smembers(String key){
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
 
    /**
     * 将数据放入set缓存
     * @param key 键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sadd(String key, Object...values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
 
 
    /**
     * 移除值为value的
     * @param key 键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long srem(String key, Object ...values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    //===============================list=================================
 
    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束  0 到 -1代表所有值
     */
    public List<Object> lrange(String key, long start, long end){
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
 
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     */
    public boolean rpush(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     */
    public boolean lpush(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lrem(String key,long count,Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

三、SpringBoot 的 Redisson 

Redisson官方文档: https://github.com/redisson/redisson/wiki

3.1、在之前的 Configuration 里添加 Bean

@Configuration
public class RedisConfig {
	
	// 锁前缀	
    private static final String SCHEMA_PREFIX = "redis://";
	
	// 超时时间
	private final long lockWatchTimeOut = 3000;	
	
	/**
     * 创建 RedisTemplate,注入IOC容器
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置数据源的连接工厂(默认会传入框架中自带的(也就是读取完配置文件装配的)LettuceConnectionFactory)
        // 也可以自己定义,注入容器,再通过@Qualifier("")传进来 
        template.setConnectionFactory(factory);
        //设置key的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        return template;
    }

   /**
     * 创建 RedissonClient,注入IOC容器
     */
    @Bean
    public RedissonClient redissonClient(RedisProperties redisProperties) {
        Config config = new Config();
        RedisProperties.Sentinel sentinel = redisProperties.getSentinel();
        RedisProperties.Cluster redisPropertiesCluster = redisProperties.getCluster();
        if (redisPropertiesCluster != null) {
            //集群redis
            ClusterServersConfig clusterServersConfig = config.useClusterServers();
            for (String cluster : redisPropertiesCluster.getNodes()) {
                clusterServersConfig.addNodeAddress(SCHEMA_PREFIX + cluster);
            }
            if (StringUtils.hasText(redisProperties.getPassword())) {
                clusterServersConfig.setPassword(redisProperties.getPassword());
            }
            clusterServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            clusterServersConfig.setPingConnectionInterval(30000);
        } else if (StringUtils.hasText(redisProperties.getHost())) {
            //单点redis
            SingleServerConfig singleServerConfig = config.useSingleServer().
                    setAddress(SCHEMA_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort());
            if (StringUtils.hasText(redisProperties.getPassword())) {
                singleServerConfig.setPassword(redisProperties.getPassword());
            }
            singleServerConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            singleServerConfig.setPingConnectionInterval(30000);
            singleServerConfig.setDatabase(redisProperties.getDatabase());
        } else if (sentinel != null) {
            //哨兵模式
            SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
            sentinelServersConfig.setMasterName(sentinel.getMaster());
            for (String node : sentinel.getNodes()) {
                sentinelServersConfig.addSentinelAddress(SCHEMA_PREFIX + node);
            }
            if (StringUtils.hasText(redisProperties.getPassword())) {
                sentinelServersConfig.setPassword(redisProperties.getPassword());
            }
            sentinelServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
            sentinelServersConfig.setPingConnectionInterval(30000);
            sentinelServersConfig.setDatabase(redisProperties.getDatabase());
        }
        config.setLockWatchdogTimeout(lockWatchTimeOut);
        return Redisson.create(config);
    }

}

3.2、分布式锁

Redisson续期机制—看门狗机制:
1.启动定时任务重新给锁设置过期时间,默认过期时间是 30 秒,每 10 秒(默认事件的1/3)续期一次(补到 30 秒)
2.如果线程挂掉(服务器宕机),则不会续期。
3.只有lock.lock(); 会有看门狗机制;
4.lock.lock(10,,TimeUnit.SECONDS);手动设置过期时间的话,则不会有看门狗机制。

/**
 * 分布式Redis锁
 */
@Slf4j
public class DistributedRedisLock {

    @Autowired
    private RedissonClient redissonClient;

    // 加锁
    public Boolean lock(String lockName) {
        if (redissonClient == null) {
            log.info("DistributedRedisLock redissonClient is null");
            return false;
        }
        try {
            RLock lock = redissonClient.getLock(lockName);
            // 锁15秒后自动释放,防止死锁
            lock.lock(15, TimeUnit.SECONDS);
            // 加锁成功
            return true;
        } catch (Exception e) {
			e.printStackTrace();
            return false;
        }
    }

    // 释放锁
    public Boolean unlock(String lockName) {
        if (redissonClient == null) {
            log.info("DistributedRedisLock redissonClient is null");
            return false;
        }
        try {
            RLock lock = redissonClient.getLock(lockName);
            lock.unlock();
            // 释放锁成功
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

3.3、读写锁 

@Autowired
RedissonClient redisson;

@Autowired
RedisTemplate redisTemplate;

@ResponseBody
@GetMapping("/write")
public String writeValue(){
	RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
	RLock rLock = lock.writeLock();
	String s = "";
	try {
		s = UUID.randomUUID().toString();
		// 模拟业务时间    
		Thread.sleep(30000);
	} catch (Exception e){
		e.printStackTrace();
	}finally {
		rLock.unlock();
	}
	redisTemplate.opsForValue().set("writeValue",s);
	return s;
}

@GetMapping(value = "/read")
@ResponseBody
public String readValue() {
	String s = "";
	RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
	//加读锁
	RLock rLock = readWriteLock.readLock();
	try {
		rLock.lock();
		s = (String) redisTemplate.opsForValue().get("writeValue");
		TimeUnit.SECONDS.sleep(10);
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		rLock.unlock();
	}
	return s;
}

3.4、闭锁 

@GetMapping(value = "/lockDoor")
@ResponseBody
public String lockDoor() throws InterruptedException {
	RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
	lockDoor.trySetCount(5); // 设置计数为5
	lockDoor.await(); //等待闭锁完成
	return "放假啦...";
}

@GetMapping(value = "/go/{id}")
public String go(@PathVariable("id") Integer id)  {
	RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
	lockDoor.countDown(); // 计数减1
	return id+"班都走光了";
}

3.5、信号量 

@GetMapping(value = "/park")
@ResponseBody
public String park() {
	RSemaphore park = redisson.getSemaphore("park");
	try {
		park.acquire();// 获取一个信号量(redis中信号量值-1),如果redis中信号量为0了,则在这里阻塞住,直到信号量大于0,可以拿到信号量,才会继续执行。
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	return "ok";
}

@GetMapping(value = "/go")
@ResponseBody
public String go() {
	RSemaphore park = redisson.getSemaphore("park");
	park.release();  //释放一个信号量(redis中信号量值+1)
	return "ok";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/894917.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux中ubuntu安装hashcat方法以及使用GPU破解

一、linux安装hashcat git clone https://github.com/hashcat/hashcat.git make make install二、安装驱动 驱动版本安装大全&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 查看自己显卡选择对应的版本&#xff0c;根据下面命令无脑安装就行了 wget https:/…

誉天华为数通Datacom HCIE认证课程

作为当前网络领域最具含金量的认证之一&#xff0c;华为Datacom HCIE认证不仅是网络工程师们技术能力的证明&#xff0c;也是未来行业趋势的风向标。 尽管经历了几度版本更迭、考题变化&#xff0c;但是誉天一直紧跟技术发展趋势&#xff0c;在专业领域和教学内容上始终与当前的…

生成国密密钥对

在线生成国密密钥对 生成的密钥对要妥善保管&#xff0c;丢失是无法找回的。

奇迹MU服务端IGC架设流程

奇迹MU服务端IGC架设流程 大家好我是艾西&#xff0c;今天跟大家简单聊下奇迹IGC服务端。IGC端是国外的东西全英文大家感兴趣的话也可以自己研究研究&#xff0c;整体的东西还是非常完整。扩展以及端的稳定性还是非常不错的就是有点费脑子........&#xff08;此教程为个人娱乐…

WebStorm运行vue项目

WebStorm运行vue项目&#xff08;vue2&#xff09; 1.安装webstorm 2. 需要安装node.js 环境&#xff0c;可以去官网下载 https://nodejs.org/en 3. 安装完需要查看 按winr 输入cmd进入 输入命令node -v 和npm -v 查看&#xff0c;会出现相应的版本号,代表安装成功 vue官网安…

IC设计中主要的EDA工具有哪些? (内附EDA虚拟机安装资源)

EDA工具的使用涵盖了芯片的功能设计、综合、验证、物理设计等环节&#xff0c;更是被称作“芯片设计的工作母机”。下面就来为大家具体介绍一下常见的EDA工具。&#xff08;需要EDA虚拟机安装资源文末可领取~&#xff09; 什么是EDA&#xff1f; EDA是电子设计自动化&#xf…

对象存储服务-MinIO基本集成

是什么 MinIO 是一个高性能的分布式对象存储服务&#xff0c;适合存储非结构化数据&#xff0c;如图片&#xff0c;音频&#xff0c;视频&#xff0c;日志等。对象文件最大可以达到5TB。 安装启动 mkdir -p /usr/local/minio cd /usr/local/minio# 下载安装包 wget https:/…

NextJs - Error Handling (错误处理)

Error.js 的作用 使用 error.js 文件规则&#xff0c;可以优雅地处理嵌套路由中的意外运行时错误。 在 React Error Boundary 中自动封装路由段及其嵌套子段。 使用文件系统层次结构来调整粒度&#xff0c;为特定网段量身定制错误 UI。 将错误隔离到受影响的网段&#xff0…

红帆OA SQL注入漏洞复现

0x01 产品简介 红帆iOffice.net从最早满足医院行政办公需求&#xff08;传统OA&#xff09;&#xff0c;到目前融合了卫生主管部门的管理规范和众多行业特色应用&#xff0c;是目前唯一定位于解决医院综合业务管理的软件&#xff0c;是最符合医院行业特点的医院综合业务管理平…

怎么修改图片的分辨率?

怎么修改图片的分辨率&#xff1f;很多人还不知道分辨率是什么意思&#xff0c;以为代表了图片的清晰度&#xff0c;然而并不是这样的&#xff0c;其实图片的分辨率就是图片尺寸大小的意思。修改图片的分辨率即改变图片的尺寸&#xff0c;通常以像素为单位表示。分辨率决定了图…

【腾讯云Cloud Studio实战训练营】使用Cloud Studio社区版快速构建React完成点餐H5页面还原

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&#x1f4bb;上一篇…

如何优雅做好项目管理?

导言 项目本身无好坏之分&#xff0c;项目管理有做好与做坏之别。在互联网大厂的体制下&#xff0c;想要做坏一个项目很难&#xff08;可以通过换人、追加资源等方式消除风险&#xff09;&#xff0c;想要做好一个项目不容易&#xff0c;需要团队及PM付出大量心血和精力。在这…

实时业务需求和数据处理要求的满足:流处理的能力

随着数据量和数据种类的不断增加&#xff0c;企业和组织对于实时数据处理和业务响应的需求也越来越迫切。传统的批处理方式无法满足业务对于实时性和即时响应的要求&#xff0c;因此&#xff0c;流处理技术应运而生。通过流处理能力&#xff0c;可以有效地处理实时数据&#xf…

Python爬虫常用:谷歌浏览器驱动——Chromedriver 插件安装教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 我们在做爬虫的时候经常要使用谷歌浏览器驱动&#xff0c;今天分享下这个Chromedriver 插件的安装方法。 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第一步、打开谷…

VR仿真实训系统编辑平台赋予老师更多自由和灵活性

为了降低院校教师在VR虚拟现实方面应用的门槛&#xff0c;VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验&#xff0c;制作了VR动物课件编辑器&#xff0c;正在逐渐受到师生们的关注和应用。 简单来说&#xff0c;VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…

【LeetCode】696 . 计数二进制子串

696 . 计数二进制子串&#xff08;简单&#xff09; 思路 我们可以将字符串按照 0 和 1 的连续段分组&#xff0c;存在 count 数组中。例如 00111011 可以得到&#xff1a;count {2,3,1,2}。这里 count 数组中两个相邻的数一定代表的是两种不同的字符。假设 count 数组中两个相…

vue3生成点选验证码,前端校验

先看效果图 verify.vue&#xff1a;源码{用时一天半的破轮子}&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; <template><div class"outside" ref&q…

寻路算法小游戏

寻路算法小demo 寻路算法有两种&#xff0c;一种是dfs 深度优先算法&#xff0c;一种是 dfs 深度优先算法 深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义&#xff0c;深度优先&#xff0c;则是以深度为准则&#xff0c;先一条路走到底&#xff0c;直到达到目标。这…

spring的核心技术---bean的生命周期加案例分析详细易懂

目录 一.spring管理JavaBean的初始化过程&#xff08;生命周期&#xff09; Spring Bean的生命周期&#xff1a; 二.spring的JavaBean管理中单例模式及原型&#xff08;多例&#xff09;模式 2.1 . 默认为单例&#xff0c;但是可以配置多例 2.2.举例论证 2.2.1 默认单例 2.2…