SpringBoot | Redis序列化与分布式锁

发布时间 2023-09-27 09:42:45作者: LittleDonkey

欢迎参观我的博客,一个Vue 与 SpringBoot结合的产物:https://poetize.cn

原文链接:https://poetize.cn/article?id=40

RedisTemplate自定义序列化方式

@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        //ObjectMapper 将序列化对象的所有属性,不管它们的可见性如何。这对于确保所有属性都被包括在JSON中非常有用,尤其是当你需要在序列化时包括私有属性或其他非公共属性时。
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //ObjectMapper 将在序列化时包括类型信息,以便在反序列化时能够正确地还原对象的具体类型。
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);

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

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

        template.afterPropertiesSet();
        return template;
    }
}

Redisson分布式锁

可重入锁

@Slf4j
@Component
public class RedisLock {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 加锁
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean lock(String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            lock.lock();
            log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock lock [{}] Exception: {}", lockName, e.getMessage());
            return false;
        }
    }


    /**
     * 加锁
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean lock(long leaseTime, TimeUnit timeUnit, String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            //leaseTime 秒钟自动解锁,自动解锁时间一定要大于业务执行时间
            lock.lock(leaseTime, timeUnit);
            log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock lock [{}] Exception: {}", lockName, e.getMessage());
            return false;
        }
    }

    /**
     * 加锁
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean tryLock(String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            boolean b = lock.tryLock();
            log.info("Thread [{}] DistributedRedisLock lock [{}] {}", Thread.currentThread().getName(), lockName, b);
            return b;
        } catch (Exception e) {
            log.error("DistributedRedisLock lock [{}] Exception: {}", lockName, e.getMessage());
            return false;
        }
    }

    /**
     * 加锁
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean tryLock(long waitTime, long leaseTime, TimeUnit timeUnit, String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            //尝试加锁,最多等待 waitTime 秒,上锁以后 leaseTime 秒自动解锁
            boolean b = lock.tryLock(waitTime, leaseTime, timeUnit);
            log.info("Thread [{}] DistributedRedisLock tryLock [{}] {}", Thread.currentThread().getName(), lockName, b);
            return b;
        } catch (Exception e) {
            log.error("DistributedRedisLock tryLock [{}] Exception: {}", lockName, e.getMessage());
            return false;
        }
    }

    /**
     * 释放锁
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean unlock(String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    log.info("Thread [{}] DistributedRedisLock unlock [{}] success", Thread.currentThread().getName(), lockName);
                }
            }
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock unlock [{}] Exception: {}", lockName, e.getMessage());
            return false;
        }
    }

    /**
     * 判断key是否被锁定
     *
     * @param lockName 锁
     * @return Boolean
     */
    public Boolean isLock(String lockName) {
        try {
            RLock lock = redissonClient.getLock(lockName);
            boolean b = lock.isLocked();
            log.info("Thread [{}] DistributedRedisLock isLock [{}] {}", Thread.currentThread().getName(), lockName, b);
            return b;
        } catch (Exception e) {
            log.error("DistributedRedisLock isLock [{}] Exception: ", lockName, e);
            return false;
        }
    }
}

联锁

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
//同时加锁:lock1 lock2 lock3
//所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();

红锁

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
//同时加锁:lock1 lock2 lock3
//红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

看门狗

Redisson提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期,也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁超时时间,锁不会因为超时而被释放。

默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout来另行指定。另外Redisson 还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。