当前位置: 首页 > 后端技术 > Java

SpringBoot+Redis实现分布式锁,还有谁不会??_0

时间:2023-04-01 18:52:22 Java

1銆備笟鍔¤儗鏅竴浜涗笟鍔¤姹傛槸姣旇緝鑰楁椂鐨勬搷浣滐紝闇€瑕佸姞閿佷互闃叉鍚庣画鐨勫苟鍙戞搷浣溿€傚悓鏃讹紝瀵规暟鎹簱鏁版嵁鐨勬搷浣滈渶瑕侀伩鍏嶅奖鍝嶄箣鍓嶇殑涓氬姟銆?銆佸垎鏋愯繃绋嬩娇鐢≧edis浣滀负鍒嗗竷寮忛攣锛屽皢閿佺殑鐘舵€佹斁鍦≧edis涓粺涓€缁存姢锛岃В鍐充簡闆嗙兢涓崟鏈篔VM涔嬮棿淇℃伅涓嶅吋瀹圭殑闂锛岃瀹氫簡鎿嶄綔椤哄簭锛屼繚鎶や簡姝g‘鎬х殑鐢ㄦ埛鏁版嵁銆傛⒊鐞嗚璁℃祦绋嬫柊寤烘敞瑙interface锛屽湪娉ㄨВ涓缃甶nput鏍囧織澧炲姞AOP鍒囩偣锛屾壂鎻忓叿浣撴敞瑙e缓绔婡Aspect鍒囬潰浠诲姟锛屾敞鍐宐ean骞舵嫤鎴叿浣撴柟娉曞弬鏁癙roceedingJoinPoint锛屽苟鍦ㄦ柟娉昿jp.proceed()鎴彇鍒囧叆鐐逛箣鍓嶅拰涔嬪悗杩涜閿佸畾锛屼换鍔℃墽琛屽畬鎴愬悗鍒犻櫎key銆傛牳蹇冩楠わ細鍔犻攣銆佽В閿併€佸欢缁€備娇鐢≧edisTemplate鐨刼psForValue.setIfAbsent鏂规硶鍒ゆ柇鏄惁鏈塳ey锛岃缃竴涓殢鏈烘暟UUID.random()銆倀oString锛岀敓鎴愪竴涓殢鏈烘暟浣滀负鍊笺€備粠redis鑾峰彇閿佸悗锛岀粰key璁剧疆expire杩囨湡鏃堕棿锛岃繃鏈熷悗鑷姩閲婃斁閿併€傛寜鐓ц繖绉嶈璁★紝鍙湁绗竴涓姹傛垚鍔熻缃甂ey鎵嶈兘杩涜鍚庣画鐨勬暟鎹搷浣滐紝鍚庣画鐨勮姹傞兘浼氬洜涓烘棤娉曡幏鍙栶煍愯祫婧愯€屽け璐ャ€傝秴鏃堕棶棰樻媴蹇僷jp.proceed()鍒囩偣鎵ц鏂瑰紡杩囦簬鑰楁椂锛屽鑷碦edis涓殑key鍥犺秴鏃舵彁鍓嶉噴鏀俱€傛瘮濡傜嚎绋婣鍏堣幏鍙栭攣锛宲roceed鏂规硶鑰楁椂锛岃秴杩囬攣瓒呮椂鏃堕棿锛岃秴鏃堕噴鏀鹃攣銆傛鏃跺彟涓€涓嚎绋婤鎴愬姛鑾峰彇鍒癛edis閿侊紝涓や釜绾跨▼鍚屾椂鎿嶄綔鍚屼竴鎵规暟鎹紝瀵艰嚧鏁版嵁涓嶄竴鑷淬€傜簿纭殑銆傝В鍐虫柟娉曪細娣诲姞涓€涓湭瀹屾垚涓旀湭閲婃斁閿佺殑鈥滅画琛屸€濅换鍔★細缁存姢涓€涓畾鏃剁嚎绋嬫睜ScheduledExecutorService锛屾瘡2s鎵弿涓€娆″姞鍏ラ槦鍒楃殑Tasks锛屽垽鏂槸鍚︿复杩戣繃鏈熸椂闂淬€傚叕寮忎负锛歔杩囨湡鏃堕棿]<=[褰撳墠鏃堕棿]+[澶辫触闂撮殧锛堜笁鍒嗕箣涓€瓒呮椂锛塢/***绾跨▼姹狅紝姣忎釜JVM浣跨敤涓€涓嚎绋嬬淮鎶eyAliveTime锛屽畾鏃舵墽琛宺unnable*/privatestaticfinalScheduledExecutorServiceSCHEDULER=newScheduledThreadPoolExecutor(1,newBasicThreadFactory.Builder().namingPattern("redisLock-schedule-pool").daemon(true).build());static{SCHEDULER.scheduleAtFixedRate(()->{//dosomethingtoextendtime},0,2,TimeUnit.SECONDS);}3.璁捐鏂规缁忚繃浠ヤ笂鍒嗘瀽锛屽悓浜嬪皬馃悷璁捐浜嗚繖涓柟妗堬細鏁翠綋娴佺▼鍓嶉潰宸茬粡璁茶繃锛岃繖閲屾湁鍑犱釜鏍稿績姝ラ锛氭嫤鎴敞瑙RedisLock锛岃幏鍙栧繀瑕佺殑鍙傛暟閿佹搷浣滐紝缁х画鎿嶄綔缁撴潫涓氬姟锛岄噴鏀鹃攣*4銆傚湪瀹為檯鎿嶄綔涔嬪墠锛孉OP鐨勪娇鐢ㄦ柟娉曚篃宸茬粡鏁寸悊鍑烘潵浜嗭紝澶у鍙互鍙傝€冧竴涓嬨€傜浉鍏冲睘鎬х被閰嶇疆涓氬姟灞炴€ф灇涓捐缃甡publicenumRedisLockTypeEnum{/***鑷畾涔夐敭鍓嶇紑*/ONE("Business1","Test1"),TWO("Business2","Test2");绉佹湁瀛楃涓蹭唬鐮侊紱绉佹湁瀛楃涓叉弿杩帮紱RedisLockTypeEnum(Stringcode,Stringdesc){this.code=code;this.desc=desc;}publicStringgetCode(){杩斿洖浠g爜锛泒publicStringgetDesc(){杩斿洖鎻忚堪锛泒publicStringgetUniqueKey锛堝瓧绗︿覆閿級{returnString.format("%s:%s",this.getCode(),key);}}`浠诲姟闃熷垪瀛樺偍鍙傛暟`publicclassRedisLockDefinitionHolder{/***涓氬姟鍞竴閿?/privateStringbusinessKey;/****閿佸畾鏃堕棿锛堢锛?/privateLonglockTime;/****鏈€鍚庢洿鏂版椂闂达紙姣锛?/privateLonglastModifyTime;/***淇濆瓨褰撳墠绾跨▼*/rrent/privateTread;****鎬诲皾璇曟鏁?/privateinttryCount;/***褰撳墠灏濊瘯娆℃暟*/privateintcurrentCount;/***鏇存柊鏃堕棿鍛ㄦ湡锛堟绉掞級锛屽叕寮?閿佸畾鏃堕棿锛堣浆涓烘绉掞級/3*/privateLongmodifyPeriod;publicRedisLockDefinitionHolder(StringbusinessKey,LonglockTime,LonglastModifyTime,ThreadcurrentTread,inttryCount){this.inessKey=businessKey;this.lockTime=閿佸畾鏃堕棿锛泃his.lastModifyTime=lastModifyTime;this.currentTread=currentTread;this.tryCount=tryCount;(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceRedisLockAnnotation{/***鍏蜂綋鍙傛暟鏍囪瘑锛岄粯璁ゅ彇绗?涓笅鏍?/intlockFiled()default0;/***瓒呮椂閲嶈瘯*/inttryCount()default3;/***鑷畾涔夐攣绫诲瀷*/RedisLockTypeEnumtypeEnum();/***閲婃斁鏃堕棿锛屼互绉掍负鍗曚綅*/longlockTime()default30;}`鏍稿績鍒囬潰鎷︽埅鐨勬搷浣淩edisLockAspect.java杩欎釜绫诲垎涓轰笁閮ㄥ垎鏉ユ弿杩板叿浣撶殑鍔熻兘Pointcut璁剧疆/***璺緞@annotation涓〃绀烘嫤鎴叿浣撴敞瑙?/@Pointcut("@annotation(cn.sevenyuan.demo.aop.lock.RedisLockAnnotation)")publicvoidredisLockPC(){}鍓嶅悗宸﹀彸鍔犻攣鍜岄噴鏀鹃攣鍓嶉潰鍑犳瀹氫箟浜嗘垜浠鎴彇鐨勫垏鐐癸紝涓嬩竴姝ュ氨鏄湪鍓嶅悗鍋氫竴浜涜嚜瀹氫箟鍒囩偣鍚庢搷浣滐細@Around(value="redisLockPC()")publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{//瑙f瀽鍙傛暟Methodmethod=resolveMethod(pjp);RedisLockAnnotationannotation=method.getAnnotation(RedisLockAnnotation.class);RedisLockTypeEnumtypeEnum=annotation.typeEnum();Object[]params=pjp.getArgs();StringukString=params[annotation.lockFiled()].toString();//鐪佺暐寰堝鍙傛暟锛屾鏌ュ垽鏂┖StringbusinessKey=typeEnum.getUniqueKey(ukString);StringuniqueValue=UUID.randomUUID().toString();//閿佸畾瀵硅薄result=null;try{booleanisSuccess=redisTemplate.opsForValue().setIfAbsent(businessKey,uniqueValue);if(!isSuccess){thrownewException("浣犱笉鑳借繖鏍峰仛锛屽洜涓哄彟涓€涓汉宸茬粡鑾峰緱浜嗛攣=-=");}redisTemplate.expire(businessKey,annotation.lockTime(),TimeUnit.SECONDS);绾跨▼currentThread=Thread.currentThread();//灏嗘Task淇℃伅娣诲姞鍒扳€滃欢杩熲€濋槦鍒梙olderList.add(newRedisLockDefinitionHolder(businessKey,annotation.lockTime(),System.currentTimeMillis(),currentThread,annotation.tryCount()));//鎵ц涓氬姟鎿嶄綔result=pjp.proceed();//绾跨▼琚腑鏂紝鎶涘嚭寮傚父锛岃姹備腑鏂璱f(currentThread.isInterrupted()){thrownewInterruptedException("Youhadbeeninterrupted=-=");}}catch(InterruptedExceptione){鏃ュ織銆俥rror("涓柇寮傚父锛屽洖婊氫簨鍔?,e);thrownewException("涓柇寮傚父锛岃閲嶆柊鍙戦€佽姹?);}catch(Exceptione){log.error("鏈夐敊璇紝璇烽噸鏂版鏌?,e);}finally{//璇锋眰缁撴潫鍚庯紝鍒犻櫎key骞堕噴鏀鹃攣redisTemplate.delete(businessKey);log.info("閲婃斁閿侊紝businessKey涓篬"+businessKey+"]");}returnresult;}浠ヤ笂杩囩▼绠€鍗曟€荤粨锛氳В鏋愭敞瑙e弬鏁帮紝鑾峰彇娉ㄨВ鍊煎拰鏂规硶涓婄殑鍙傛暟鍊硷紝閿佸畾redis骞惰缃秴鏃舵椂闂村皢Task淇℃伅娣诲姞鍒扳€渄elay鈥濋槦鍒椾腑continuation锛岃鏂规硶鎻愬墠閲婃斁閿佸苟娣诲姞涓€涓嚎绋嬩腑鏂爣蹇楁潵缁撴潫璇锋眰锛屾渶鍚庡湪continuation鎿嶄綔杩囩▼涓噴鏀鹃攣杩欓噷浣跨敤ScheduledExecutorService鏉ョ淮鎶や竴涓嚎绋嬫潵涓嶆柇鍒ゆ柇浠诲姟[闃熷垪涓殑浠诲姟鍜屽欢闀胯秴鏃舵椂闂碷:`//鎵弿浠诲姟闃熷垪privatestaticConcurrentLinkedQueueholderList=newConcurrentLinkedQueue();/***绾跨▼姹狅紝缁存姢keyAliveTime*/privatestaticfinalScheduledExecutorServiceSCHEDULER=newScheduledThreadPoolExecutor(newThreadBictory)namingPattern("redisLock-schedule-pool").daemon(true).build());{//姣忎袱绉掓墽琛屼竴娆♀€滅户缁€濇搷浣淪CHEDULER.scheduleAtFixedRate(()->{//璁板緱鍦ㄨ繖閲屾坊鍔爐ry-catch鎶ラ敊鍚庯紝瀹氭椂浠诲姟涓嶄細鎵ц=-=IteratorIterator=HolderList.itrator();鑰?iTerator.hasnext()){redisLockDeFINILENILENILDER=ITEXEX)銆?holder==null){杩唬鍣ㄣ€傛秷闄わ紙锛?Continue;}//鍒ゆ柇key鏄惁杩樻湁鏁堛€傚鏋滄棤鏁堬紝鍒欑Щ闄ontinue;}//閫傛椂閲嶈瘯閲嶈瘯娆℃暟锛岃缃嚎绋嬭缃腑鏂簰鑱旂綉锛堬級;meaning.remove();confid涓夊垎涔嬩竴鏃堕棿longcurTime=System.currentTimeMillis();booleanshouldExtend=(holder.getLastModifyTime()+holder.getModifyPeriod())<=curTime;濡傛灉(shouldExtend){holder.setLastModifyTime(curTime);redisTemplate.expire(holder.getBusinessKey(),holder.getLockTime(),TimeUnit.SECONDS);log.info("businessKey:["+holder.getBusinessKey()+"],trycount:"+holder.getCurrentCount());Holder.setCurrentCount(holder.getCurrentCount()+1);}}}},0,2,TimeUnit.SECONDS);}`杩欐浠g爜鐢ㄤ簬瀹炵幇璁捐鍥句腑铏氱嚎妗嗙殑鎬濇兂锛岄伩鍏嶄簡涓€涓潪甯歌€楁椂鐨勮姹傦紝瀵艰嚧閿佽閲婃斁鎻愬墠銆傝繖閲岋紝娣诲姞浜嗏€滅嚎绋嬩腑鏂€漈hread#interrupt銆傚笇鏈涜秴杩囬噸璇曟鏁板悗锛屽彲浠ヤ腑鏂嚎绋嬨€備粛鐒跺彲浠ユ煡鎵炬牴鏈師鍥狅紝鍒嗘瀽鑰楁椂璺緞锛岃繘琛屼笟鍔′紭鍖栨垨鍏朵粬澶勭悊锛岄伩鍏嶈繖浜涜€楁椂鎿嶄綔銆傛墍浠ヨ寰楀璁板綍锛屽彲浠ユ洿蹇殑鍒嗘瀽闂銆傚浣曚娇鐢⊿pringBootAOP璁板綍杩愯鏃ュ織鍜屽紓甯告棩蹇楋紵5.寮€濮嬫祴璇曞湪涓€涓叆鍙f柟娉曚腑锛屼娇鐢ㄨ繖涓敞瑙o紝鐒跺悗鍦ㄤ笟鍔′腑妯℃嫙鑰楁椂璇锋眰锛屼娇鐢═hread#sleep@GetMapping("/testRedisLock")@RedisLockAnnotation(typeEnum=RedisLockTypeEnum.ONE,lockTime=3)publicBooktestRedisLock(@RequestParam("userId")LonguserId){try{log.info("beforesleep");绾跨▼.鐫$湢(10000);log.info("鐫$湢鍚?);}catch(Exceptione){//璁板綍閿欒log.info("hassomeerror",e);}returnnull;}浣跨敤鐨勬椂鍊欏湪鏂规硶涓婂姞涓婅繖涓敞瑙o紝鐒跺悗璁剧疆鐩稿簲鐨勫弬鏁板嵆鍙€傛牴鎹畉ypeEnum鍙互鍖哄垎鍚勭鏈嶅姟銆傞檺鍒朵笟鍔″悓鏃剁粡钀ャ€傛祴璇曠粨鏋滐細2020-04-0414:55:50.864INFO9326---[nio-8081-exec-1]c.s.demo.controller.BookController:beforesleepexecution2020-04-0414:55:52.855INFO9326-----[k-schedule-pool]c.s.demo.aop.lock.RedisLockAspect锛歜usinessKey锛歔Business1:1024]锛屽皾璇曡鏁帮細02020-04-0414:55:54.851INFO9326---[k-schedule-pool]c.s.demo.aop.lock.RedisLockAspect锛歜usinessKey锛歔Business1:1024]锛屽皾璇曡鏁帮細12020-04-0414:55:56.851INFO9326---[k-schedule-pool]c.s.demo.aop銆傞攣銆俁edisLockAspect锛歜usinessKey锛歔Business1锛?024]锛屽皾璇曡鏁帮細22020-04-0414锛?5锛?8.852INFO9326---[k-schedule-pool]c.s.demo.aop.lock.RedisLockAspect锛歜usinessKey锛歔Business1锛?024],trycount:32020-04-0414:56:00.857INFO9326---[nio-8081-exec-1]c.s.demo.controller.BookController:鏈変竴浜涢敊璇痡ava.lang.InterruptedException:sleepinterruptedatjava.lang.Thread.sleep(NativeMethod)[na:1.8.0_221]鎴戣繖閲屾祴璇曠殑鏄噸璇曞け璐ユ鏁拌繃澶氱殑鍦烘櫙銆傚鏋滃噺灏戜紤鐪犳椂闂达紝涓氬姟鍙互姝e父鎵ц銆傚鏋滃悓鏃惰姹傦紝浼氬彂鐜板涓嬮敊璇俊鎭細璇存槑鎴戜滑鐨勯攣馃攼纭疄鐢熸晥浜嗭紝閬垮厤閲嶅璇锋眰銆?.鎬荤粨瀵逛簬鑰楁椂鐨勪笟鍔″拰鏍稿績鏁版嵁锛屼笉鑳藉厑璁搁噸澶嶈姹傚悓鏃舵搷浣滄暟鎹紝閬垮厤鏁版嵁涓嶆纭紝鎵€浠ュ簲璇ヤ娇鐢ㄥ垎甯冨紡閿佹潵淇濇姢銆傛垜浠啀姊崇悊涓€涓嬭璁℃祦绋嬶細鏂板缓娉ㄨВ@interface锛屽湪娉ㄨВ涓缃叆鍙俧lag娣诲姞AOP鍒囩偣锛屾壂鎻忓叿浣撴敞瑙e垱寤篅Aspect鍒囬潰浠诲姟锛屾敞鍐宐ean骞舵嫤鎴叿浣撴柟娉曞叿浣撴柟娉曞弬鏁癙roceedingJoinPoint锛屽浜庢嫤鎴墠鍚庣殑鏂规硶pjp.proceed()锛屽湪鍒囧叆鐐瑰墠鍔犻攣锛屼换鍔℃墽琛屽畬鍚庡垹闄ey銆傛湰娆″涔犲熀浜巖eview灏忎紮浼寸殑浠g爜璁捐锛屼粠涓簡瑙e垎甯冨紡閿佺殑鍏蜂綋瀹炵幇銆傛寜鐓т粬鐨勮璁★紝閲嶅啓浜嗕竴涓畝鍖栫増鏈€備笟鍔″鐞嗐€傚浜庝箣鍓嶆病鏈夎€冭檻鐨勨€滅户缁€濇搷浣滐紝杩欓噷浣跨敤瀹堟姢绾跨▼瀹氭椂鍒ゆ柇骞跺欢闀胯秴鏃舵椂闂达紝閬垮厤浜嗛攣鐨勬彁鍓嶉噴鏀俱€備簬鏄紝鎴戝悓鏃跺涔犱簡涓変釜鐭ヨ瘑鐐癸細1.AOP瀹炵幇鍙婂父鐢ㄦ柟娉?.璋冨害绾跨▼姹燬cheduledExecutorService鐨勪娇鐢ㄥ強鍙傛暟鍚箟3.绾跨▼Thread#interrupt鐨勫惈涔夊強鐢ㄦ硶锛堣繖涓緢鏈夋剰鎬濓紝浣犲彲浠ユ繁鍏ュ涔犱竴娆★級