开发者

SpringBoot3+Redis实现分布式锁的配置方法

开发者 https://www.devze.com 2024-08-10 10:54 出处:网络 作者: piaoyunlive
目录SpringBoot3+Redis+Lua脚本实现分布式锁 相关依赖包application.yml Redis配置Lua脚本获取锁脚本 tryLock.lua释放锁脚本 releaseLock.luaService层实现Controller调用示例代码SpringBoot3+Redisson实现分布
目录
  • SpringBoot3+Redis+Lua脚本实现分布式锁 
    • 相关依赖包
      • application.yml Redis配置
    • Lua脚本
      • 获取锁脚本 tryLock.lua
      • 释放锁脚本 releaseLock.lua
    • Service层实现
      • Controller调用示例代码
        • SpringBoot3+Redisson实现分布式锁
          • 添加依赖包
          • 配置类
          • 实现类

      SpringBoot3+Redis+Lua脚本实现分布php式锁 

      相关依赖包

      <spring-boot.version>3.0.2</spring-boot.version>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>

      application.yml Redis配置

      spring:
        data:
          redis:
            host: 192.168.5.133
            port: 6379
            password:
            database: 0
            lettuce:
              pool:
                max-active: 8
                max-idle: 8
                min-idle: 0
                max-wait: "-1ms"

      Lua脚本

      获取锁脚本 tryLock.lua

      -- KEYS[1] 是锁的键
      -- ARGV[1] 是请求ID
      -- ARGV[2] 是锁的过期时间(秒)
      local lockKey = KEYS[1]
      local requestId = ARGV[1]
      local expireTime = tonumber(ARGV[2])
      -- 尝试获取锁
      local lockValue = redis.call('GET', lockKey)
      -- 如果锁不存在,尝试设置锁
      if not lockValue then
          if redis.call('SETNX', lockKey, requestId) then
              -- 设置锁的过期时间
              redis.call('EXPIRE', lockKey, expipythonreTime)
              return 1
          end
          return 0
      elseif lockValue == requestId then
          -- 如果请求ID与当前锁持有者匹配,延长锁的过期时间
          redis.call('EXPIRE', lockKey, expireTime)
          return 1
      else
          -- 锁被其他请求持有,无法获取锁
          return 0
      end

      释放锁脚本 releaseLock.lua

      -- KEYS[1] 是锁的键
      -- ARGV[1] 是请求ID
      local lockKey = KEYS[1]
      local requestId = ARGV[1]
      -- 获取锁的值
      local lockValue = redis.call('GET', lockKey)
      -- 检查请求ID是否匹配锁的持有者
      if lockValue == requestId then
          -- 删除锁
          redis.call('DEL', lockKey)
          return 1
      elsewww.devze.com
          return 0
      end

      Service层实现

      package pub.qingyun.service;
      import jakarta.annotation.Resource;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.core.io.ClassPathResource;
      import org.springframework.data.redis.core.StringRedisTemplate;
      import org.springframework.data.redis.core.script.DefaultRedisScript;
      import org.springframework.scripting.support.ResourceScriptSource;
      import org.springframework.stereotype.Service;
      import Java.util.List;
      /**
       * @author CQY
       * @version 1.0
       * @date 2024/7/10 10:29
       **/
      @Service
      @Slf4j
      public class RedisLockService {
          private static final String LOCK_KEY = "distributed-lock";
          @Resource
          private StringRedisTemplate stringRedisTemplate;
          private static final DefaultRedisScript<Long> TRY_LOCK_SCRIPT = new DefaultRedisScript<>();
          private static final DefaultRedisScript<Long> RELEASE_LOCK_SCRIPT = new DefaultRedisScript<>();
          static {
              TRY_LOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/tryLock.lua")));
              TRY_LOCK_SCRIPT.setResultType(Long.class);
              RELEASE_LOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));
              RELEASE_LOCK_SCRIPT.setResultType(Long.class);
          }
          /**
           * 尝试获取分布式锁。
           *
           * @param requestId  请求ID,用于唯一标识锁的持有者。
           * @param expireTime 锁的过期时间(秒)。
           * @return 如果成功获取锁返回true,否则返回false。
           */
          public boolean tryLock(String requestId, int expireTime) {
              Object result = stringRedisTemplate.execute(TRY_LOCK_SCRIPT,
                      List.of(LOCK_KEY),
                      requestId,
                      String.valueOf(expireTime));
              assert result != null;
              return Long.parseLong(result.toString()) == 1L;
          }
          /**
           * 释放分布式锁。
           *
           * @param requestId 请求ID,必须与获取锁时使用的相同。
           * @return 如果锁成功释放返回true,否则返回false。
           */
          public boolean releaseLock(String requestId) {
              Object result = stringRedisTemplate.execute(RELEASE_LOCK_SCRIPT,
                      List.of(LOCK_KEY),
                      requestId);
              assert result != null;
              return Long.parseLong(result.toString()) == 1L;
          }
      }

      Controller调用示例代码

      package pub.qingyun.controller;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import pub.qingyun.service.RedisLockService;
      import java.util.UUID;
      import java.util.concurrent.TimeUnit;
      /**
       * @author CQY
       * @version 1.0
       * @date 2024/7/10 10:43
       **/
      @Slf4j
      @RestController
      public class LuaLockController {
          // Lock timphpeout in seconds
          private static final int LOCK_TIMEOUT_SECONDS = 30000;
          private final RedisLockService lockService;
          @Autowired
          public LuaLockController(RedisLockService lockService) {
              this.lockService = lockService;
          }
          /**
           * 尝试获取锁并执行一些操作,然后释放锁。
           * 通过尝试获取锁来确保操作的原子性,避免并发问题
           */
          @GetMapping("/performOperation")
          public String performOperation() {
              // 使用UUID作为请求ID
              String requestId = UUID.randomUUID().toString();
              try {
                  // 尝试获取锁
                  boolean tryLock = lockService.tryLock(requestId, LOCK_TIMEOUT_SECONDS);
                  log.info("获取锁[{}][{}]", requestId, tryLock);
                  if (tryLock) {
                      // 执行关键操作
                      log.info("开始执行主任务[{}]...", requestId);
                      TimeUnit.SECONDS.sleep(5); // 模拟耗时操作
                      log.info("任务[{}]执行完成", requestId);
                      return requestId + " completed successfully.";
                  } else {
                      log.info("无法获取锁,任务[{}]被拒绝", requestId);
                      return "无法获取锁,任务[" + requestId + "]被拒绝";
                  }
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
                  log.error("Interrupted while performing operation.", e);
                  phpreturn "任务[" + requestId + "]执行失败";
              } finally {
                  // 释放锁
                  boolean releaseLock = lockService.releaseLock(requestId);
                  log.info("释放锁[{}][{}]", requestId, releaseLock);
              }
          }
      }

      SpringBoot3+Redisson实现分布式锁

      添加依赖包

      <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson-spring-boot-starter</artifactId>
          <version>3.20.0</version>
      </dependency>

      配置类

      @Configuration
      package pub.qingyun.config;
      import org.redisson.Redisson;
      import org.redisson.api.RedissonClient;
      import org.redisson.config.Config;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.connection.RedisConnectionFactory;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.serializer.GenericJackson2jsonRedisSerializer;
      import org.springframework.data.redis.serializer.StringRedisSerializer;
      /**
       * @author CQY
       * @version 1.0
       * @date 2024/7/5 10:58
       **/
      @Configuration
      public class RedisConfig {
          @Value("${spring.data.redis.host}")
          private String host;
          @Value("${spring.data.redis.port}")
          private String port;
          @Bean
          public RedissonClient redissonClient() {
              Config config = new Config();
              config.useSingleServer().setAddress("redis://" + host + ":" + port + "");
              return Redisson.create(config);
          }
          @Bean
          public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
              RedisTemplate<String, Object> template = new RedisTemplate<>();
              template.setConnectionFactory(connectionFactory);
              template.setKeySerializer(new StringRedisSerializer());
              template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
              template.afterPropertiesSet();
              return template;
          }
      }

      实现类

       	@Resource
          private RedissonClient redissonClient;
      	public void example() {
              RLock rlock = redissonClient.getLock("myLock");
              try {
                  boolean locked = rlock.tryLock(0, 800, TimeUnit.MILLISECONDS);
                  if (locked) {
                      // TODO
                  } else {
                      log.warn("Thread[{}]Could not acquire lock.", Thread.currentThread().getName());
                  }
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
                  log.error("Error occurred while trying to acquire lock.", e);
              } finally {
                  if (rlock.isHeldByCurrentThread()) {
                      rlock.unlock();
                  }
              }
          }

      到此这篇关于SpringBoot3+Redis实现分布式锁的文章就介绍到这了,更多相关SpringBoot3 Redis分布式锁内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号