springboot集成redis( lettuce + redisson) 实现redis存储和分布式锁

当你看到这篇文章的时候,心里会想同一个项目为啥要用2个不同的redis客户端工具,是不是有什么大病???
jedis:客户端实例不是线程安全的,需要借助连接池来管理和使用 Jedis,还有它是阻塞的I/O。不支持异步。
lettuce:首先它是异步和非阻塞的,还支持多种特性,例如:事务、流水线操作、发布/订阅、Lua 脚本等
redisson:对字符串的支持比较差,不支持排序、事务、管道、分区等
所以综上所述,lettuce + redisson 一起使用且不是美滋滋吗?

提前准备

  • 使用的springboot版本3.2.7,引入依赖
  <!-- redisson ,排除了lettuce,jedis客户端 -->
 <dependency>
       <groupId>org.redisson</groupId>
       <artifactId>redisson-spring-boot-starter</artifactId>
       <version>3.32.0</version>
</dependency>


 <!-- redis : ,spring boot 默认使用lettuce客户端 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

配置lettuce

配置yml

spring:
  data:
    redis:
      host: localhost
      port: 6379
      timeout: 10s
      password: redis@123
      database: 12
      lettuce:
          pool:
            # 连接池中的最小空闲连接
            min-idle: 0
            # 连接池中的最大空闲连接
            max-idle: 8
            # 连接池的最大数据库连接数
            max-active: 8
            # #连接池最大阻塞等待时间(使用负值表示没有限制)
            max-wait: -1ms

编写redis配置文件 RedisConfig

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }

}

redis的工具类 RedisCache

@Component
public class RedisCache {

    @Autowired
    public RedisTemplate redisTemplate;


    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }


    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
    }


    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }


    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }


    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }


    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }


    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }


    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

}

使用

    @Resource
    private RedisCache redisCache;


    @Test
    void redisUser() {
        Long userId = 1813144155037069314L;
        String key = "USER_KEY:";
        UserEntity redisUserEntity = redisCache.getCacheObject(key + userId);
        if (ObjectUtil.isNull(redisUserEntity)) {
            UserEntity userEntity = userMapper.selectOne(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getUserId, userId));
            if (ObjectUtil.isNotNull(userEntity)) {
                redisCache.setCacheObject(key + userEntity.getUserId(), userEntity);
                log.info("设置缓存成功");
            }
        }
    }

到此完成一半,

配置redisson

编写RedissonConfig配置文件

@Configuration
public class RedissonConfig {


    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private String port;

    @Value("${spring.data.redis.password}")
    private String password;

    @Value("${spring.data.redis.database}")
    private Integer database;


    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(RedissonClient.class)
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port)
                // 更多.set
                .setPassword(password)
                .setDatabase(database)
        ;
        return Redisson.create(config);
    }

编写RedisLock锁工具类

@Component
public class RedisLock {

    @Autowired
    private RedissonClient redissonClient;



    /**
     * 获取锁
     *
     * @param lockKey 锁实例key
     * @return 锁信息
     */
    public RLock getRedisLock(String lockKey)
    {
        return redissonClient.getLock(lockKey);
    }

    /**
     * 加锁
     *
     * @param lockKey 锁实例key
     * @return 锁信息
     */
    public RLock lock(String lockKey)
    {
        RLock lock = getRedisLock(lockKey);
        lock.lock(); // 阻塞式等待。默认加的锁是30s时间
        return lock;
    }

    /**
     * 加锁
     *
     * @param lockKey 锁实例key
     * @param leaseTime 上锁后自动释放锁时间
     * @return true=成功;false=失败
     */
    public Boolean tryLock(String lockKey, long leaseTime)
    {
        return tryLock(lockKey, 0, leaseTime, TimeUnit.SECONDS);
    }

    /**
     * 加锁
     *
     * @param lockKey 锁实例key
     * @param leaseTime 上锁后自动释放锁时间
     * @param unit 时间颗粒度
     * @return true=加锁成功;false=加锁失败
     */
    public Boolean tryLock(String lockKey, long leaseTime, TimeUnit unit)
    {
        return tryLock(lockKey, 0, leaseTime, unit);
    }

    /**
     * 加锁
     *
     * @param lockKey 锁实例key
     * @param waitTime 最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @param unit 时间颗粒度
     * @return true=加锁成功;false=加锁失败
     */
    public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
    {
        RLock rLock = getRedisLock(lockKey);
        boolean tryLock = false;
        try
        {
            tryLock = rLock.tryLock(waitTime, leaseTime, unit);
        }
        catch (InterruptedException e)
        {
            return false;
        }
        return tryLock;
    }

    /**
     * 释放锁
     *
     * @param lockKey 锁实例key
     */
    public void unlock(String lockKey)
    {
        RLock lock = getRedisLock(lockKey);
        lock.unlock();
    }

    /**
     * 释放锁
     *
     * @param lock 锁信息
     */
    public void unlock(RLock lock)
    {
        lock.unlock();
    }

使用

    @Resource
    private RedisLock redisLock;
 private static final String LOCK_USER_KEY = "queryUserKey";
    int n = 500;
    @Override
    public Result<String> queryUserLock() throws InterruptedException {
        //阻塞式等待。默认加的锁是30s时间
        RLock lock = redisLock.lock(LOCK_USER_KEY);
        try {
            log.info("线程:" + Thread.currentThread().getName() + "获得了锁");
            log.info("剩余数量:{}", --n);
        } finally {
            // 做释放锁操作的时候判断一下是否已成功加锁 && lock.isLocked()是判断自己lock对象声明的锁是否被锁定
            判断是否有锁对象,以及是否是同一个锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                //释放锁
                lock.unlock();
                log.info("线程:" + Thread.currentThread().getName() + "已经开始释放锁了!!!");
            }
        }
        return Result.success();
    }

jmeter测试结果

并发5000,3秒,都完成ok,只截部分
 并发5000,3秒,都完成ok

还有一种tryLock方法,是非阻塞的,有兴趣的朋友可以去了解一下。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值