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

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

时间:2023-04-02 09:14:51 Java

浣滆€咃細jingQ\鏉ユ簮锛歨ttps://www.sevenyuan.cn/涓€銆佷笟鍔¤儗鏅竴浜涗笟鍔¤姹傛槸姣旇緝鑰楁椂鐨勬搷浣滐紝闇€瑕佸姞閿佷互闃叉鍚庣画骞跺彂鎿嶄綔銆傚悓鏃跺鏁版嵁搴撲腑鐨勬暟鎹繘琛屾搷浣溿€傞渶瑕侀伩鍏嶅涔嬪墠鐨勪笟鍔¢€犳垚褰卞搷銆?銆佸垎鏋愯繃绋嬩娇鐢≧edis浣滀负鍒嗗竷寮忛攣锛屽皢閿佺殑鐘舵€佹斁鍦≧edis涓粺涓€缁存姢锛岃В鍐充簡闆嗙兢涓崟鏈篔VM涔嬮棿淇℃伅涓嶅吋瀹圭殑闂锛岃瀹氫簡鎿嶄綔椤哄簭锛屼繚鎶や簡姝g‘鎬х殑鐢ㄦ埛鏁版嵁銆傛⒊鐞嗚璁℃祦绋嬫柊寤烘敞瑙interface锛屽湪娉ㄨВ涓缃甶nput鏍囧織澧炲姞AOP鍒囩偣锛屾壂鎻忓叿浣撴敞瑙e缓绔婡Aspect鍒囬潰浠诲姟锛屾敞鍐宐ean骞舵嫤鎴叿浣撴柟娉曞弬鏁癙roceedingJoinPoint锛屽苟鍦ㄦ柟娉昿jp.proceed()鎴彇鍒囧叆鐐逛箣鍓嶅拰涔嬪悗杩涜閿佸畾锛屼换鍔℃墽琛屽畬鎴愬悗鍒犻櫎key銆傛牳蹇冩楠わ細鍔犻攣锛岃В閿侊紝缁х画鍔犻攣浣跨敤RedisTemplate鐨刼psForValue.setIfAbsent鏂规硶鍒ゆ柇鏄惁鏈塳ey锛岃缃竴涓殢鏈烘暟UUID.random().toString锛岀敓鎴愪竴涓殢鏈烘暟浣滀负value銆備粠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锛岃幏鍙栧繀瑕佺殑鍙傛暟閿佹搷浣滐紝缁х画鎿嶄綔缁撴潫涓氬姟锛岄噴鏀鹃攣銆?.鍦ㄥ疄闄呮搷浣滀箣鍓嶏紝AOP鐨勪娇鐢ㄦ柟娉曚篃宸茬粡鏁寸悊濂戒簡銆傚彲浠ュ弬鑰冪浉鍏冲睘鎬х被閰嶇疆涓氬姟灞炴€ф灇涓捐缃畃ublicenumRedisLockTypeEnum{/***鑷畾涔夐敭鍓嶇紑*/ONE("Business1","Test1"),TWO("Business2","Test2");绉佹湁瀛楃涓蹭唬鐮侊紱绉佹湁瀛楃涓叉弿杩帮紱RedisLockTypeEnum(Stringcode,Stringdesc){this.code=code;杩欎釜.desc=desc;}娴licStringgetCode(){杩斿洖浠g爜锛泒publicStringgetDesc(){杩斿洖鎻忚堪锛泒publicStringgetUniqueKey(Stringkey){杩斿洖瀛楃涓层€俧ormat("%s:%s",this.getCode(),key);}}浠诲姟闃熷垪淇濆瓨鍙傛暟publicclassRedisLockDefinitionHolder{/***涓氬姟鍞竴閿?/privateStringbusinessKey;/***閿佸畾鏃堕棿锛堢锛?/privateLonglockTime;/***鏈€鍚庢洿鏂版椂闂达紙姣锛?/privateLonglastModifyTime;/***淇濆瓨褰撳墠绾跨▼*/privateThreadcurrentTread;/***鎬诲皾璇曟鏁?/privateinttryCount;/***褰撳墠灏濊瘯娆℃暟*/privateintcurrentCount;/***鏇存柊鏃堕棿鍛ㄦ湡锛堟绉掞級锛屽叕寮?閿佸畾鏃堕棿锛堣浆涓烘绉掞級/3*/privateLongmodifyPeriod;publicRedisLockDefinitionHolder(StringbusinessKey,LonglockTime,LonglastModifyTime,ThreadcurrentTread,inttryCount){this.businessKey=businessKey;this.lockTime=閿佸畾鏃堕棿锛泃his.lastModifyTime=lastModifyTime;杩欎釜.currentTread=currentTread;this.tryCount=tryCount;this.modifyPeriod=lockTime*1000/3;}}璁剧疆鎷︽埅鐨勬敞瑙e悕绉癅Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceRedisLockAnnotation{/***鍏蜂綋鍙傛暟鏍囪瘑锛岄粯璁や娇鐢ㄧ0涓笅鏍?/intlockFiled()榛樿涓?锛?***瓒呮椂閲嶈瘯娆℃暟*/inttryCount()default3;/***鐢变簬瀹氫箟浜嗛攣绫诲瀷*/RedisLockTypeEnumtypeEnum();/***閲婃斁鏃堕棿锛屼互绉掍负鍗曚綅*/longlockTime()default30;}鏍稿績鍒囬潰鎷︽埅鐨勬搷浣淩edisLockAspect.java杩欎釜绫诲垎涓轰笁閮ㄥ垎鏉ユ弿杩板叿浣撶殑鍔熻兘PointcutsettingDefined/***@annotation涓殑path琛ㄧず鎷︽埅鍏蜂綋鐨勬敞瑙?/@Pointcut("@annotation(cn.sevenyuan.demo.aop.lock.RedisLockAnnotation)")publicvoidredisLockPC(){}Aroundlock鍜屼笂涓€姝ラ噴鏀鹃攣瀹氫箟鎴戜滑瑕佹埅鍙栫殑鍒囩偣銆傛帴涓嬫潵灏辨槸鍦ㄥ垏鐐瑰墠鍚庡仛涓€浜涜嚜瀹氫箟鎿嶄綔锛欯Around(value="redisLockPC()")publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{//瑙f瀽鍙傛暟Methodmethod=resolveMethod(pjp);RedisLockAnnotationannotation=method.getAnnotation(绾㈣壊isLockAnnotation.class);RedisLockTypeEnumtypeEnum=annotation.typeEnum();瀵硅薄[]鍙傛暟=pjp.getArgs();瀛楃涓瞮kString=params[annotation.lockFiled()].toString();//鐪佺暐璁稿鍙傛暟鏍¢獙鍜孍mptyStringbusinessKey=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();//灏嗚繖涓猅ask淇℃伅娣诲姞鍒扳€滃欢杩熲€濋槦鍒梙olderList.add(newRedisLockDefinitionHolder(businessKey,annotation.lockTime(),System.currentTimeMillis(),currentThread,annotation.tryCount()));//鎵ц涓氬姟鎿嶄綔result=pjp.proceed();//绾跨▼琚腑鏂紝鎶涘嚭寮傚父锛岃姹備腑鏂璱f(currentThread.isInterrupted()){thrownewInterruptedException("Youhadbeeninterrupted=-=");}}catch(InterruptedExceptione){log.error("涓柇寮傚父锛屽洖婊氫簨鍔?,e);thrownewException("涓柇寮傚父锛岃閲嶆柊鍙戦€佽姹?);}catch(Exceptione){log.error("鏈夐敊璇紝璇烽噸鏂版鏌?,e);}finally{//璇锋眰缁撴潫鍚庯紝鍒犻櫎key骞堕噴鏀鹃攣redisTemplate.delete(businessKey);log.info("閲婃斁閿侊紝businessKey涓篬"+businessKey+"]");}returnresult;}绠€鍗曟€荤粨涓€涓嬩笂闈㈢殑杩囩▼锛氳В鏋愭敞瑙e弬鏁帮紝鑾峰彇娉ㄨВ鍊煎拰鏂规硶涓婄殑鍙傛暟鍊硷紝redis鍔犻攣骞惰缃秴鏃舵椂闂达紝灏嗚繖涓猅ask淇℃伅娣诲姞鍒扳€渄elay鈥濋槦鍒楋紝浠ュ強continue鐨勬椂鍊欙紝閫氳繃鎻愬墠閲婃斁閿侊紝娣诲姞涓€涓嚎绋嬩腑鏂爣蹇楁潵缁撴潫璇锋眰锛屽湪finally閲婃斁閿佺户缁繍琛屻€傝繖閲屼娇鐢⊿cheduledExecutorService缁存姢涓€涓嚎绋嬶紝涓嶆柇鍒ゆ柇浠诲姟闃熷垪涓殑浠诲姟骞跺欢闀胯秴鏃舵椂闂达細//鎵弿浠诲姟闃熷垪privatestaticConcurrentLinkedQueueholderList=newConcurrentLinkedQueue();/***绾跨▼姹狅紝缁存姢keyAliveTime*/privatestaticfinalScheduledExecutorServiceSCHEDULER=newScheduledThreadPoolExecutor(1,newBasicThreadFactory.Builder().namingPattern("redisLock-schedule-pool").daemon(true).build());{//姣忎袱绉掓墽琛屼竴娆♀€滅户缁€濇搷浣淪CHEDULER.scheduleAtFixedRate(()->{//杩欓噷璁板緱鍔爐ry-catch锛屽惁鍒欐姤閿欏悗涓嶄細鎵ц瀹氭椂浠诲姟=-=Iteratoriterator=holderList.iterator();while(iterator.hasNext()){RedisLockDefinitionHolderholder=iterator.next();//emptyif(holder==null){iterator.remove();缁х画;}//鍒ゆ柇key鏄惁杩樻湁鏁堬紝濡傛灉涓嶆湁鏁堝垯绉婚櫎if(redisTemplate.opsForValue().get(holder.getBusinessKey())==null){iterator.remove();continue;}//瓒呮椂閲嶈瘯娆℃暟瓒呰繃褰撶粰绾跨▼璁剧疆涓柇鏃秈f(holder.getCurrentCount()>holder.getTryCount()){holder.getCurrentTread().interrupt();杩唬鍣?remove();缁х画;}//鍒ゆ柇鏄惁杩涘叆鏈€鍚庝笁鍒嗕箣涓€鐨勬椂闂磍ongcurTime=System.currentTimeMillis();甯冨皵鍊約houldExtend=(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锛屽笇鏈涘湪閲嶈瘯娆℃暟瓒呰繃鍚庯紝鍙互涓柇绾跨▼锛堟湭涓ユ牸娴嬭瘯锛屼粎渚涘弬鑰冨搱鍝堝搱鍝堬級涓嶈繃锛屽缓璁亣鍒拌繖鏍风殑鏃跺€欌€斺€旇€楁椂鐨剅equest锛岃繕鏄彲浠ヤ粠鏍规簮涓婃壘鍘熷洜锛屸€嬧€嬪垎鏋愯€楁椂璺緞锛岃繘琛屼笟鍔′紭鍖栨垨鍏朵粬澶勭悊锛岄伩鍏嶈繖浜涜€楁椂鎿嶄綔銆傛墍浠ヨ寰楀璁板綍锛屽彲浠ユ洿蹇殑鍒嗘瀽闂銆傚浣曚娇鐢⊿pringBootAOP璁板綍杩愯鏃ュ織鍜屽紓甯告棩蹇楋紵鎴戜笉浼氫粙缁峉pringBoot鐨勫熀纭€鐭ヨ瘑銆傛帹鑽愯繖涓疄鐢ㄦ暀绋嬶細https://github.com/javastacks...5.鍦ㄤ竴涓叆鍙f柟娉曞紑濮嬫祴璇曪紝浣跨敤杩欎釜娉ㄨВ锛岀劧鍚庡湪涓氬姟涓ā鎷熻€楁椂璇锋眰锛屼娇鐢═hread#sleep@GetMapping("/testRedisLock")@RedisLockAnnotation(typeEnum=RedisLockTypeEnum.ONE,lockTime=3)publicBooktestRedisLock(@RequestParam("userId")LonguserId){try{log.info("鎵ц鍓嶄紤鐪?);绾跨▼.鐫$湢(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鐨勫惈涔夊強鐢ㄦ硶锛堣繖涓緢鏈夋剰鎬濓紝鍙互娣卞叆瀛︿範鐐瑰嚮锛夊弬鑰冭祫鏂欙細https://blog.csdn.net/XWForev...https://www.zhihu.com/questio...杩戞湡鐑偣鏂囩珷鎺ㄨ崘锛?.1,000+Java闈㈣瘯闂瓟锛?022鏈€鏂扮増锛?.鍔茬垎锛丣ava鍗忕▼鏉ヤ簡銆?.3.SpringBoot2.x鏁欑▼锛屽お鍏ㄩ潰浜嗭紒4.涓嶈鐢ㄧ垎鐮寸垎婊$敾闈紝璇曡瘯瑁呴グ鑰呮ā寮忥紝杩欐墠鏄紭闆呯殑鏂瑰紡锛侊紒5.銆奐ava寮€鍙戞墜鍐岋紙宓╁北鐗堬級銆嬫渶鏂板彂甯冿紝璧跺揩涓嬭浇鍚э紒鎰熻涓嶉敊锛屽埆蹇樹簡鐐硅禐+杞彂锛?/p>