一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

基于Redis實現(xiàn)分布式鎖

 頭號碼甲 2020-12-11

我們知道分布式鎖的特性是排他、避免死鎖、高可用。分布式鎖的實現(xiàn)可以通過數(shù)據(jù)庫的樂觀鎖(通過版本號)或者悲觀鎖(通過for update)、Redis的setnx()命令、Zookeeper(在某個持久節(jié)點添加臨時有序節(jié)點,判斷當前節(jié)點是否是序列中最小的節(jié)點,如果不是則監(jiān)聽比當前節(jié)點還要小的節(jié)點。如果是,獲取鎖成功。當被監(jiān)聽的節(jié)點釋放了鎖(也就是被刪除),會通知當前節(jié)點。然后當前節(jié)點再嘗試獲取鎖,如此反復)

 
redis.png

本篇文章,主要講如何用Redis的形式實現(xiàn)分布式鎖。后續(xù)文章會講解熱點KEY讀取,緩存穿透和緩存雪崩的場景和解決方案、緩存更新策略等等知識點,理論知識點較多。

Redis配置

spring:
  redis:
    port: 6379
    database: 0
    host: 127.0.0.1
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
    timeout: 5000ms 

Jedis工具類

public class JedisConnectionUtil {

    private static JedisPool pool = null;

    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(100);
        pool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
    }

    public static Jedis getJedis(){
        return pool.getResource();
    }
}

  

分布式鎖分析與編碼

下面進入正文。因為分布式系統(tǒng)之間是不同進程的,單機版的鎖無法滿足要求。所以我們可以借助中間件Redis的setnx()命令實現(xiàn)分布式鎖。setnx()命令只會對不存在的key設值,返回1代表獲取鎖成功。對存在的key設值,會返回0代表獲取鎖失敗。這里的value是System.currentTimeMillis() (獲取鎖的時間)+鎖持有的時間。我這里設置鎖持有的時間是200ms,實際業(yè)務執(zhí)行的時間遠比這200ms要多的多,持有鎖的客戶端應該檢查鎖是否過期,保證鎖在釋放之前不會過期。因為客戶端故障的情況可能是很復雜的。比如現(xiàn)在有A,B倆個客戶端。A客戶端獲取了鎖,執(zhí)行業(yè)務中做了騷操作導致阻塞了很久,時間應該遠遠超過200ms,當A客戶端從阻塞狀態(tài)下恢復繼續(xù)執(zhí)行業(yè)務代碼時,A客戶端持有的鎖由于過期已經(jīng)被其他客戶端占有。這時候A客戶端執(zhí)行釋放鎖的操作,那么有可能釋放掉其他客戶端的鎖。

我這里設置的客戶端等待鎖的時間是200ms。這里通過輪詢的方式去讓客戶端獲取鎖。如果客戶端在200ms之內(nèi)沒有鎖的話,直接返回false。實際場景要設置合適的客戶端等待鎖的時間,避免消耗CPU資源。

獲取鎖

/**
 * 獲取鎖
 * @param lockName 鎖的名字
 * @param acquireTimeout 或得所的超時時間
 * @param lockTimeout 所本身的超時時間
 * @return
 */
public String acquireLock(String lockName, long acquireTimeout, long lockTimeout){
    // 鎖標識
    String identifler = UUID.randomUUID().toString();
    String lockKey = "lock" + lockName;
    int lockExpire = (int) (lockTimeout / 1000);
    Jedis jedis = null;

    try {
        jedis = JedisConnectionUtil.getJedis();

        long end = System.currentTimeMillis() + acquireTimeout;
        // 獲取鎖的限定時間
        while (System.currentTimeMillis() < end) {
            if (jedis.setnx(lockKey, identifler) == 1) {
                // 設置成功
                // 設置超時時間
                jedis.expire(lockKey, lockExpire);
                // 或得鎖成功
                return identifler;
            }
            if (jedis.ttl(lockKey) == -1) {
                // 如果一直沒有或得鎖設置超時時間
                jedis.expire(lockKey, lockExpire);
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }finally {
        jedis.close();
    }
    return null;
}

  

所以,我們要加上鎖過期,然后獲取鎖的策略。通過realKey獲取當前的currentValue。currentValue也就是獲取鎖的時間 + 鎖持有的時間。 如果currentValue不等于null 且 currentValue 小于當前時間,說明鎖已經(jīng)過期。這時候如果突然來了C,D兩個客戶端獲取鎖的請求,不就讓C,D兩個客戶端都獲取鎖了嗎。如果防止這種現(xiàn)象發(fā)生,我們采用getSet()命令來解決。getSet(key,value)的命令會返回key對應的value,然后再把key原來的值更新為value。也就是說getSet()返回的是已過期的時間戳。如果這個已過期的時間戳等于currentValue,說明獲取鎖成功。
 

釋放鎖

/**
 * 釋放鎖
 * @param lockName 鎖的名字
 * @param identifler 鎖標識
 * @return
 */
public boolean releaseLock(String lockName, String identifler){
    System.out.println(lockName + "釋放鎖" + identifler);
    String lockKey = "lock:" + lockName;
    Jedis jedis = null;
    boolean isrelease = false;
    try {
        jedis = JedisConnectionUtil.getJedis();
        while (true){
            jedis.watch(lockKey);
            // 判斷是否是同一把鎖
            if (identifler.equals(jedis.get(lockKey))){
                Transaction transaction = jedis.multi();
                transaction.del(lockKey);
                if (transaction.exec().isEmpty()){
                    continue;
                }
                isrelease = true;
            }
            // TODO 異常
            jedis.unwatch();
            break;
        }
    }finally {
        jedis.close();
    }
    return isrelease;
}

編寫測試類用多線程模擬高并發(fā)

public class UnitTest extends Thread {

    @Override
    public void run() {
        while (true){
            DistributedLock distributedLock = new DistributedLock();
            String rs = distributedLock.acquireLock("updateOrder", 2000, 5000);
            if (rs != null){
                System.out.println(Thread.currentThread().getName() + "-> 或得鎖" + rs);
                try {
                    Thread.sleep(1000);
                    distributedLock.releaseLock("updateOrder", rs);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    public static void main(String[] args) {
        UnitTest unitTest = new UnitTest();
        for (int i = 0; i < 10; i++) {
            new Thread(unitTest, "tName:" + i).start();
        }
    }
}

代碼運行結(jié)果

tName:0-> 或得鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab
updateOrder釋放鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab
tName:1-> 或得鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0
updateOrder釋放鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0
tName:6-> 或得鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f
updateOrder釋放鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f
tName:2-> 或得鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca
updateOrder釋放鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca
tName:4-> 或得鎖094f921d-fe9b-46ba-873b-aee2ce974f16
updateOrder釋放鎖094f921d-fe9b-46ba-873b-aee2ce974f16
tName:5-> 或得鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9
updateOrder釋放鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9
tName:9-> 或得鎖678e2099-651c-4e23-a648-9fb588ecb42b
updateOrder釋放鎖678e2099-651c-4e23-a648-9fb588ecb42b
tName:7-> 或得鎖f35cdbad-6fde-4f1e-a4c1-321805a39374
updateOrder釋放鎖f35cdbad-6fde-4f1e-a4c1-321805a39374
tName:3-> 或得鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072
updateOrder釋放鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072
tName:8-> 或得鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce
updateOrder釋放鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce
 
 
 
本文文字部分參考文章:https://www.jianshu.com/p/83224c0f3bb9

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产级别精品一区二区视频| 91精品视频免费播放| 高清一区二区三区大伊香蕉 | 亚洲欧美日本国产有色| 国产一区二区三中文字幕| 中文字幕一二区在线观看| 久久大香蕉精品在线观看| 真实偷拍一区二区免费视频| 日本办公室三级在线观看| 男人和女人黄 色大片| 内用黄老外示儒术出处| 国产一区国产二区在线视频| 99香蕉精品视频国产版| 国产人妻熟女高跟丝袜| 日韩成人高清免费在线| 超碰在线免费公开中国黄片| 国产精品九九九一区二区| 国产精品日韩精品一区| 亚洲中文字幕视频一区二区| 日韩在线免费看中文字幕| 国产欧美日本在线播放| 亚洲国产成人爱av在线播放下载| 日韩精品福利在线观看| 色综合伊人天天综合网中文| 成人区人妻精品一区二区三区| 狠狠亚洲丁香综合久久| 国产精品免费不卡视频| 国产伦精品一区二区三区高清版| 国产一区二区三区精品免费| 午夜视频成人在线观看| 日韩毛片视频免费观看| 日本东京热视频一区二区三区| 欧美日韩国产欧美日韩| 日韩人妻毛片中文字幕| 欧美日韩综合免费视频| 国产精品免费视频久久| 亚洲一区二区三区免费的视频| 国产一区二区三区草莓av| 91熟女大屁股偷偷对白| 熟女一区二区三区国产| 中国美女偷拍福利视频|