欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot+redis+lua實現(xiàn)分布式鎖的腳本

 更新時間:2024年11月28日 11:15:49   作者:2301_79308687  
本文介紹了如何使用Spring Boot、Redis和Lua腳本實現(xiàn)分布式鎖,包括實現(xiàn)原理、代碼實現(xiàn)和存在的問題,感興趣的朋友跟隨小編一起看看吧

1 分布式鎖

Java鎖能保證一個JVM進程里多個線程交替使用資源。而分布式鎖保證多個JVM進程有序交替使用資源,保證數(shù)據(jù)的完整性和一致性。
分布式鎖要求

互斥。一個資源在某個時刻只能被一個線程訪問。避免死鎖。避免某個線程異常情況不釋放資源,造成死鎖??芍厝?。高可用。高性能。非阻塞,沒獲取到鎖直接返回失敗。

2 實現(xiàn)

1 lua腳本

為了實現(xiàn)redis操作的原子性,使用lua腳本。為了方便改腳本,將腳本單獨寫在文件里。

-- 加鎖腳本
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
    redis.call('pexpire', KEYS[1], ARGV[2]);
    return true;
else
    return false;
end
-- 解鎖腳本
if redis.call('get', KEYS[1]) == ARGV[1] then
    redis.call('del', KEYS[1]);
    return true;
else
    return false;
end
-- 更新鎖腳本
if redis.call('get', KEYS[1]) == ARGV[1] then
    redis.call('pexpire', KEYS[1], ARGV[2]);
    -- pexpire與expire的區(qū)別是:pexpire毫秒級,expire秒級
    return true;
else
    return false;
end

將腳本裝在Springboot容器管理的bean里。

@Configuration
public class RedisConfig {
    @Bean("lock")
    public RedisScript<Boolean> lockRedisScript() {
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Boolean.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/ratelimit/lock.lua")));
        return redisScript;
    }
    @Bean("unlock")
    public RedisScript<Boolean> unlockRedisScript() {
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Boolean.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/ratelimit/unlock.lua")));
        return redisScript;
    }
    @Bean("refresh")
    public RedisScript<Boolean> refreshRedisScript() {
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Boolean.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/ratelimit/refresh.lua")));
        return redisScript;
    }
}

redis分布式鎖業(yè)務(wù)類

@Service
public class LockService {
    private static final long LOCK_EXPIRE = 30_000;
    private static final Logger LOGGER = LoggerFactory.getLogger(LockService.class);
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    @Qualifier("lock")
    private RedisScript<Boolean> lockScript;
    @Autowired
    @Qualifier("unlock")
    private RedisScript<Boolean> unlockScript;
    @Autowired
    @Qualifier("refresh")
    private RedisScript<Boolean> refreshScript;
    public boolean lock(String key, String value) {
        boolean res = redisTemplate.execute(lockScript, List.of(key), value, LOCK_EXPIRE);
        if (res == false) {
            return false;
        }
        refresh(key, value);
        LOGGER.info("lock, key: {}, value: {}, res: {}", key, value, res);
        return res;
    }
    public boolean unlock(String key, String value) {
        Boolean res = redisTemplate.execute(unlockScript, List.of(key), value);
        LOGGER.info("unlock, key: {}, value: {}, res: {}", key, value, res);
        return res != null && Boolean.TRUE.equals(res);
    }
    private void refresh(String key, String value) {
        Thread t = new Thread(() -> {
            while (true) {
                redisTemplate.execute(refreshScript, List.of(key), value, LOCK_EXPIRE);
                try {
                    Thread.sleep(LOCK_EXPIRE / 2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("refresh, current time: {}, key: {}, value: {}", System.currentTimeMillis(), key, value);
            }
        });
        t.setDaemon(true); // 守護線程
        t.start();
    }
}

測試類

@SpringBootTest(classes = DemoApplication.class)
public class LockServiceTest {
    @Autowired
    private LockService service;
    private int count = 0;
    @Test
    public void test() throws Exception {
        List<CompletableFuture<Void>> taskList = new ArrayList<>();
        for (int threadIndex = 0; threadIndex < 10; threadIndex++) {
            CompletableFuture<Void> task = CompletableFuture.runAsync(() -> addCount());
            taskList.add(task);
        }
        CompletableFuture.allOf(taskList.toArray(new CompletableFuture[0])).join();
    }
    public void addCount() {
        String id = UUID.randomUUID().toString().replace("-", "");
        boolean tryLock = service.lock("account", id);
        while (!tryLock) {
            tryLock = service.lock("account", id);
        }
        for (int i = 0; i < 10_000; i++) {
            count++;
        }
        try {
            Thread.sleep(100_000);
        } catch (Exception e) {
            System.out.println(e);
        }
        for (int i = 0; i < 3; i++) {
            boolean releaseLock = service.unlock("account", id);
            if (releaseLock) {
                break;
            }
        }
    }
}

3 存在的問題

這個分布式鎖實現(xiàn)了互斥,redis鍵映射資源,如果存在鍵,則資源正被某個線程持有。如果不存在鍵,則資源空閑。
避免死鎖,靠的是設(shè)置reds鍵的過期時間,同時開啟守護線程動態(tài)延長redis鍵的過期時間,直到該線程任務(wù)完結(jié)。
高性能。redis是內(nèi)存數(shù)據(jù)庫,性能很高。同時lua腳本使得redis以原子性更新鎖狀態(tài),避免多次spirngboot與redis的網(wǎng)絡(luò)IO。
非阻塞。lock()方法沒有獲取到鎖立即返回false,不會阻塞當前線程。

沒有實現(xiàn)可重入和高可用。高可用需要redis集群支持。

到此這篇關(guān)于springboot+redis+lua實現(xiàn)分布式鎖的文章就介紹到這了,更多相關(guān)springboot redis lua分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot3+SpringSecurity6前后端分離的項目實踐

    SpringBoot3+SpringSecurity6前后端分離的項目實踐

    SpringSecurity6 的用法和以前版本的有較大差別,本文主要介紹了SpringBoot3+SpringSecurity6前后端分離的項目實踐,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Java語言的安裝、配置、編譯與運行過程

    Java語言的安裝、配置、編譯與運行過程

    本文詳細介紹了如何在Windows、macOS和Linux操作系統(tǒng)上安裝、配置Java開發(fā)環(huán)境(JDK),并展示了如何編寫、編譯和運行Java程序,同時,還提供了常見問題的解決方案,正確配置Java環(huán)境對Java開發(fā)至關(guān)重要,是進行Java編程的基礎(chǔ)
    2025-02-02
  • java隨機數(shù)生產(chǎn)算法實例

    java隨機數(shù)生產(chǎn)算法實例

    下面小編就為大家?guī)硪黄猨ava隨機數(shù)生產(chǎn)算法實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • 一問詳解SpringBoot配置文件優(yōu)先級

    一問詳解SpringBoot配置文件優(yōu)先級

    在SpringBoot項目當中,我們要想配置一個屬性,可以通過這三種方式當中的任意一種來配置都可以,那么優(yōu)先級怎么算,本文主要介紹了一問詳解SpringBoot配置文件優(yōu)先級,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • 簡單易懂的java8新特性之lambda表達式知識總結(jié)

    簡單易懂的java8新特性之lambda表達式知識總結(jié)

    一直想針對lambda表達式作一個總結(jié),借助于這次公司安排的考試作一個入門式的總結(jié),對正在學習java的小伙伴們非常有幫助,需要的朋友可以參考下
    2021-05-05
  • java爬蟲之使用HttpClient模擬瀏覽器發(fā)送請求方法詳解

    java爬蟲之使用HttpClient模擬瀏覽器發(fā)送請求方法詳解

    這篇文章主要介紹了java爬蟲之使用HttpClient模擬瀏覽器發(fā)送請求方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 使用多種方式實現(xiàn)遍歷HashMap的方法

    使用多種方式實現(xiàn)遍歷HashMap的方法

    下面小編就為大家?guī)硪黄褂枚喾N方式實現(xiàn)遍歷HashMap的方法。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • Java總結(jié)篇系列:Java泛型詳解

    Java總結(jié)篇系列:Java泛型詳解

    下面小編就為大家?guī)硪黄狫ava總結(jié)篇系列:Java泛型詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • windows?java?-jar無法啟動jar包簡單的解決方法

    windows?java?-jar無法啟動jar包簡單的解決方法

    這篇文章主要介紹了windows?java?-jar無法啟動jar包簡單的解決方法,文中通過代碼介紹的非常詳細,對大家學習或者使用java具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-12-12
  • java 排序算法之選擇排序

    java 排序算法之選擇排序

    本文主要講解了java 排序算法之選擇排序,選擇排序是最簡單直觀的一種算法,想要了解相關(guān)知識的朋友快來看一看這篇文章吧
    2021-09-09

最新評論