SpringBoot3+Redis實現(xiàn)分布式鎖的配置方法
更新時間:2024年07月12日 10:33:35 作者:piaoyunlive
這篇文章主要介紹了SpringBoot3+Redis實現(xiàn)分布式鎖,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
SpringBoot3+Redis+Lua腳本實現(xiàn)分布式鎖
相關(guān)依賴包
<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) -- 如果鎖不存在,嘗試設(shè)置鎖 if not lockValue then if redis.call('SETNX', lockKey, requestId) then -- 設(shè)置鎖的過期時間 redis.call('EXPIRE', lockKey, expireTime) return 1 end return 0 elseif lockValue == requestId then -- 如果請求ID與當(dāng)前鎖持有者匹配,延長鎖的過期時間 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 else return 0 end
Service層實現(xiàn)
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,用于唯一標(biāo)識鎖的持有者。 * @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調(diào)用示例代碼
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 timeout in seconds private static final int LOCK_TIMEOUT_SECONDS = 30000; private final RedisLockService lockService; @Autowired public LuaLockController(RedisLockService lockService) { this.lockService = lockService; } /** * 嘗試獲取鎖并執(zhí)行一些操作,然后釋放鎖。 * 通過嘗試獲取鎖來確保操作的原子性,避免并發(fā)問題 */ @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) { // 執(zhí)行關(guān)鍵操作 log.info("開始執(zhí)行主任務(wù)[{}]...", requestId); TimeUnit.SECONDS.sleep(5); // 模擬耗時操作 log.info("任務(wù)[{}]執(zhí)行完成", requestId); return requestId + " completed successfully."; } else { log.info("無法獲取鎖,任務(wù)[{}]被拒絕", requestId); return "無法獲取鎖,任務(wù)[" + requestId + "]被拒絕"; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("Interrupted while performing operation.", e); return "任務(wù)[" + requestId + "]執(zhí)行失敗"; } finally { // 釋放鎖 boolean releaseLock = lockService.releaseLock(requestId); log.info("釋放鎖[{}][{}]", requestId, releaseLock); } } }
SpringBoot3+Redisson實現(xiàn)分布式鎖
添加依賴包
<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; } }
實現(xiàn)類
@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(); } } }
到此這篇關(guān)于SpringBoot3+Redis實現(xiàn)分布式鎖的文章就介紹到這了,更多相關(guān)SpringBoot3 Redis分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Redis實現(xiàn)記錄訪問次數(shù)的三種方案
這篇文章主要介紹了使用Redis實現(xiàn)記錄訪問次數(shù)的三種方案,文中通過代碼示例和圖文講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-09-09淺談一下Redis的數(shù)據(jù)結(jié)構(gòu)
這篇文章主要介紹了淺談一下Redis的數(shù)據(jù)結(jié)構(gòu),簡單字符串結(jié)構(gòu)被用于存儲redis的key對象和String類型的value對象,其中的free和len字段可以輕松的使得在該字符串被修改時判斷是否需要擴容,需要的朋友可以參考下2023-08-08Redis的五種基本類型和業(yè)務(wù)場景和使用方式
Redis是一種高性能的鍵值存儲數(shù)據(jù)庫,支持多種數(shù)據(jù)結(jié)構(gòu)如字符串、列表、集合、哈希表和有序集合等,它提供豐富的API和持久化功能,適用于緩存、消息隊列、排行榜等多種場景,Redis能夠?qū)崿F(xiàn)高速讀寫操作,尤其適合需要快速響應(yīng)的應(yīng)用2024-10-10Redis集群水平擴展、集群中添加以及刪除節(jié)點的操作
這篇文章主要介紹了Redis集群水平擴展、集群中添加以及刪除節(jié)點的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03Redis五種數(shù)據(jù)結(jié)構(gòu)在JAVA中如何封裝使用
本篇博文就針對Redis的五種數(shù)據(jù)結(jié)構(gòu)以及如何在JAVA中封裝使用做一個簡單的介紹。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11