spring boot 整合 redis 实现分布式锁

spring boot 整合 redis 实现分布式锁

利用redis setnx 命令的特性实现分布式锁

  • SETNX key value

    只在键 key 不存在的情况下, 将键 key 的值设置为 value

    若键 key 已经存在, 则 SETNX 命令不做任何动作。

    SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

  • 返回值

    命令在设置成功时返回 1 , 设置失败时返回 0

  • 设置key 过期时间,防止死锁

具体实现代码

  • lock接口

    public interface Lock {
    
        /**
         获取锁,直至成功
         */
         String getLock(String key, Long ttl);
        /**
         * 尝试获取锁,成功获取锁之后,会返回一个非空的字符串,解锁时需要传
         * @param key
         * @param ttl 占有锁的时间,单位秒
         * @return
         */
        String tryLock(String key, Long ttl);
    
        /**
         * 释放锁
         * @param key 获取锁时对应的key
         * @param value 获取锁时返回的非空字符串
         * @return
         */
        Boolean unLock(String key, String value);
    
  • redis实现

    @Component
    @Slf4j
    public class RedisDistributedLock implements Lock {
    
        private static final  String commandSet="set";
        private static final  String commandNX="NX";
        private static final  String commandEX="EX";
        private static  final  String script="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "       return 0 end";
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        /**
         *
         * @param key
         * @param ttl 占有锁的时间,单位秒
         * @return
         */
        @Override
        public String tryLock(String key, Long ttl) {
            String value = UUID.fastUUID().toString();
            Boolean isSuccess = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
                RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
                RedisSerializer keySerializer = redisTemplate.getKeySerializer();
                Object result = redisConnection.execute(commandSet, keySerializer.serialize(key),
                        valueSerializer.serialize(value),
                        commandNX.getBytes(StandardCharsets.UTF_8),
                        commandEX.getBytes(StandardCharsets.UTF_8),
                        String.valueOf(ttl).getBytes(StandardCharsets.UTF_8));
                return !ObjectUtils.isEmpty(result);
            });
    
            return ObjectUtils.nullSafeEquals(isSuccess, Boolean.TRUE) ? value : null;
        }
    
        /**
         * 使用lua脚本,确保原子性操作
         * @param key   获取锁时对应的key
         * @param value 获取锁时返回的非空字符串
         * @return
         */
        @Override
        public Boolean unLock(String key, String value) {
            DefaultRedisScript<Boolean> defaultRedisScript = new DefaultRedisScript<>();
            defaultRedisScript.setResultType(Boolean.class);
            defaultRedisScript.setScriptText(script);
    
            return (Boolean) redisTemplate.execute(defaultRedisScript, Arrays.asList(key), value);
        }
    
        //获取锁 直至成功
        @Override
        public String getLock(String key, Long ttl) {
            String lockUuid;
            while (StrUtil.isBlank(lockUuid = this.tryLock(key, ttl))) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    log.error("RedisDistributedLock--getLock --异常");
                }
            }
            return lockUuid;
        }
    
    
  • 测试

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class AppApiApplicationTest {
    
    
        @Inject
        private Lock lock;
    
        @Test
        public void  testLock(){
            String lockKey="member:15814841700";
            String tryLock = lock.tryLock(lockKey, 60L);
            if (StrUtil.isEmpty(tryLock)){
                throw new BusinessException("重复请求");
            }
            try {
                System.out.println("执行任务---");
            }catch (Exception e){
    
            }finally {
                lock.unLock(lockKey,tryLock);
            }
    
        }
    

其他实现方案

  • redisson

    也是基于redis实现

  • zookeeper

    基于zk节点特性(持久顺序节点)+ watch机制

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot实现Redis分布式锁可以通过以下步骤: 1. 添加Redis依赖:在pom.xml文件中添加Spring Data Redis依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置Redis连接:在application.properties或application.yml文件中配置Redis连接信息。 ```yaml spring.redis.host=your-redis-host spring.redis.port=your-redis-port ``` 3. 创建分布式锁工具类:创建一个用于获取和释放分布式锁的工具类。 ```java @Component public class RedisLock { @Autowired private StringRedisTemplate redisTemplate; public boolean tryLock(String key, String value, long expireTime) { Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(expireTime)); return result != null && result; } public void releaseLock(String key, String value) { String currentValue = redisTemplate.opsForValue().get(key); if (currentValue != null && currentValue.equals(value)) { redisTemplate.delete(key); } } } ``` 4. 使用分布式锁:在需要加锁的地方使用分布式锁。 ```java @Autowired private RedisLock redisLock; public void doSomethingWithLock() { String key = "lockKey"; String value = UUID.randomUUID().toString(); long expireTime = 5000; // 锁的过期时间,单位为毫秒 try { if (redisLock.tryLock(key, value, expireTime)) { // 获得锁成功,执行业务逻辑 // ... } else { // 获得锁失败,执行其他逻辑 // ... } } finally { redisLock.releaseLock(key, value); } } ``` 这样就可以在Spring Boot中使用Redis实现分布式锁了。需要注意的是,分布式锁实现可能涉及到更复杂的情况,比如锁的重入性、防止死锁等,需要根据具体的业务场景进行适当的扩展和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值