日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

分布式锁编写及调试分析

發(fā)布時(shí)間:2024/4/13 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式锁编写及调试分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
那我們現(xiàn)在開始來(lái)寫一個(gè)分布式鎖,那這個(gè)方法叫就叫V2,第二個(gè)版本,/*** 可能出現(xiàn)死鎖,雖然在執(zhí)行close的時(shí)候有防死鎖,但是還是會(huì)出現(xiàn),繼續(xù)演進(jìn)V3*/ // @Scheduled(cron="0 */1 * * * ?")//每1分鐘(每個(gè)1分鐘的整數(shù)倍)public void closeOrderTaskV2() throws InterruptedException {long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));//鎖5秒有效期//這個(gè)時(shí)間如何用呢,看下面。和時(shí)間戳結(jié)合起來(lái)用。Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() +lockTimeout));if(setnxResult != null && setnxResult.intValue() == 1){//如果返回值是1,代表設(shè)置成功,獲取鎖closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);}else{log.info("沒有獲得分布式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);}}我們這個(gè)分布式鎖要鎖多久,lockTimeout,鎖的超時(shí)時(shí)間,實(shí)際生產(chǎn)環(huán)境肯定不能設(shè)置成這么長(zhǎng)時(shí)間了,我們分布式鎖就鎖5秒,那么這個(gè)時(shí)間怎么用呢,我們要配合時(shí)間戳一起來(lái)使用,RedisShardedPoolUtil我們封裝了一個(gè)setnx,這個(gè)方法和set方法差不多,public static Long setnx(String key, String value) {ShardedJedis jedis = null;Long result = null;try {jedis = RedisShardedPool.getJedis();result = jedis.setnx(key, value);} catch (Exception e) {log.error("setnx key:{} value:{} error", key, value, e);RedisShardedPool.returnBrokenResource(jedis);return result;}RedisShardedPool.returnResource(jedis);return result;}也就是我們要set這個(gè)鎖,如果鎖的key不存在的話,他才會(huì)設(shè)置成功,我們聲明鎖的一個(gè)key,public interface REDIS_LOCK{String CLOSE_ORDER_TASK_LOCK = "CLOSE_ORDER_TASK_LOCK"; //關(guān)閉訂單分布式鎖}把現(xiàn)在的時(shí)間轉(zhuǎn)換成字符串,現(xiàn)在的毫秒數(shù),再加上lockTimeout,現(xiàn)在的lockTime是50秒,如果設(shè)置成功了,就代表我獲取這個(gè)鎖了,如果沒有獲取鎖,就打印一個(gè)日志,沒有獲得分布式鎖,獲取鎖之后我們要怎么做,我們?cè)谙旅媛暶饕粋€(gè)方法,這個(gè)方法是什么呢,如果我在這里面直接調(diào)用closeOrder,但是他有一個(gè)問(wèn)題,這個(gè)鎖并沒有釋放,也就是說(shuō)我們?cè)O(shè)置的這個(gè)鎖呢,是沒有有效期的,我們拿到的是永久,也就是第一次啟動(dòng)定時(shí)任務(wù)的時(shí)候,設(shè)置上鎖,那以后再執(zhí)行到這里的時(shí)候呢,這里面拿到的肯定是0,因?yàn)檫@個(gè)key已經(jīng)存在,setnx返回的肯定是失敗,那我們這里面就存在了問(wèn)題,那我們現(xiàn)在來(lái)封裝一下,我們要把這個(gè)鎖設(shè)置一個(gè)有效期,那我們寫成private就OK了,因?yàn)橹辉趦?nèi)部調(diào)用,private void closeOrder(String lockName){ // expire命令用于給該鎖設(shè)定一個(gè)過(guò)期時(shí)間,用于防止線程crash,導(dǎo)致鎖一直有效,從而導(dǎo)致死鎖。RedisShardedPoolUtil.expire(lockName,50);//有效期50秒,防死鎖log.info("獲取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));iOrderService.closeOrder(hour);RedisShardedPoolUtil.del(lockName);//釋放鎖log.info("釋放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());log.info("=============================");}這里面?zhèn)饕粋€(gè)lockName,鎖的名字,我們要調(diào)用expire方法,把這個(gè)key要設(shè)置一個(gè)有效期,50秒,有效期50秒,防止死鎖,也就是我肯定要把這個(gè)鎖設(shè)置一個(gè)有效期的,那即使我們現(xiàn)在設(shè)置50秒,也是OK的,當(dāng)然線上的時(shí)候我們要把它改成5秒,這里面也是因?yàn)?秒太短,我們還看不到那個(gè)鎖,可能就過(guò)期了,我在關(guān)閉訂單獲取鎖之后,設(shè)置一個(gè)有效期,在這個(gè)鎖失效的時(shí)候呢,這個(gè)鎖也就釋放了,打印日志,當(dāng)前線程的名字,代表哪個(gè)線程獲取了這個(gè)鎖,然后把這個(gè)時(shí)間拿過(guò)來(lái),既然我們執(zhí)行完了關(guān)閉完了訂單,即使我們沒有超過(guò)50秒,訂單十分少,那么我們也不能讓他等待,我們要及時(shí)的釋放鎖,著呢嗎釋放呢,我們直接來(lái)刪除就可以了,把這個(gè)lock進(jìn)行一個(gè)刪除,也就是我們要主動(dòng)釋放這個(gè)分布式鎖,我們主要是關(guān)注分布式鎖,這個(gè)邏輯執(zhí)不執(zhí)行都OK,我們分析一下,首先TOMCAT1里面這個(gè)定時(shí)任務(wù)開始啟動(dòng),然后他獲取到了分布式鎖,又釋放掉了,TOMCAT2啟動(dòng),但是他沒有獲取分布式鎖,然后這個(gè)定時(shí)任務(wù)就結(jié)束了,也就是說(shuō)在調(diào)用tast的時(shí)候,直接走到else里邊,我們?cè)趫?zhí)行分布式鎖的時(shí)候,可以看到,設(shè)置完分布式鎖,然后就設(shè)置了他的有效期,然后立刻就刪除了,我們看一下redis里面,REDIS也是分布式的,這次通過(guò)一致性算法,存到了REDIS里面,6379的端口,那我們看一下他的時(shí)間,看到他現(xiàn)在是-1

這個(gè)時(shí)候設(shè)置有效期,這里主要是為了防止死鎖,我們的鎖肯定要有一個(gè)有效期的,redis自動(dòng)來(lái)釋放他,因?yàn)槲覀冊(cè)赥OMCAT2里面設(shè)置好了,所以這里返回值是0,失敗,可以看到現(xiàn)在都是空的

因?yàn)門OMCAT2已經(jīng)把lock鎖刪除掉,也就是TOMCAT2獲取了分布式鎖,我們看一下現(xiàn)在V2版本有什么問(wèn)題,這里邊要說(shuō)的是,當(dāng)我們setnx成功之后,這個(gè)lock已經(jīng)存到redis里面了,但是我們的lock并沒有有效期,然后tomcat就重啟了,關(guān)閉了,tomcat2也重啟關(guān)閉了,而這個(gè)時(shí)候我們?cè)诜植际降沫h(huán)境下,這個(gè)分布式鎖就發(fā)生了死鎖,這個(gè)鎖根本就不會(huì)釋放,因?yàn)檫€沒有走到expire這個(gè)方法里面,前提是說(shuō),也就是setnx已經(jīng)執(zhí)行完成,這樣我們這個(gè)版本的分布式鎖就存在了死鎖的問(wèn)題,雖然我們?cè)赾loseOrder里面,這里面設(shè)置了有效期,防止死鎖,但是在一定條件下,還是會(huì)發(fā)生死鎖的,那我們有一種折中的方案,關(guān)閉tomcat有兩種方式,一種我們找到tomcat進(jìn)程,來(lái)對(duì)他進(jìn)行一個(gè)kill,這種是直接把進(jìn)程關(guān)掉了,那么還有一種溫柔的方式,就是調(diào)用tomcat的shutdown方法,那如果調(diào)用shutdown方法的話,我們還有一種方式來(lái)解決他,在最上邊我們聲明一個(gè)方法,delLock方法,刪除鎖,我們把刪除鎖的代碼拿過(guò)來(lái),放到這里邊然后我們要加一個(gè)注解,@PreDestroy,這個(gè)注解是干什么用的呢,也就是說(shuō)當(dāng)我們沒有使用kill進(jìn)程的方式,來(lái)關(guān)閉tomcat,也就是溫柔的關(guān)閉tomcat的時(shí)候,使用tomcat的shutdown命令,關(guān)閉tomcat,那tomcat容器會(huì)調(diào)用predestory,也就是說(shuō)在毀滅之前,再調(diào)用這個(gè)方法,那么這里面就會(huì)執(zhí)行了,那同樣的也能起到這個(gè)效果,但是這種方式也有弊端,如果我這里關(guān)閉1千個(gè)鎖和1萬(wàn)個(gè)鎖,那我們?cè)趕hutdown的時(shí)候呢,這個(gè)時(shí)間會(huì)非常長(zhǎng),這里面用了一個(gè)極限的思維,要關(guān)閉的東西非常的多,如果我們直接kill掉tomcat的進(jìn)程,那這個(gè)方法根本不會(huì)執(zhí)行,也就沒有用武之地了,現(xiàn)在來(lái)看雖然V2做了分布式鎖,但是在防死鎖方面,是有問(wèn)題,在一定情況下,就是剛剛說(shuō)的,還是防不住死鎖的,所以我們分布式鎖,繼續(xù)演進(jìn),我們來(lái)形成V3版本的分布式鎖

?

總結(jié)

以上是生活随笔為你收集整理的分布式锁编写及调试分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。