問題の説明
このプロジェクトは、単一のテスト環境での定期的なタスク、問題なく展開します。2台のクラスタ化されたサーバの生産は、モジュールプロジェクトの展開アップが同時に定期的なタスクは、他の事故の原因となります二つのマシン上で再びそれを実装することがわかりました。
ソリューション---- Redisの分散ロック
使用Redisのは、ロック、唯一の指定された予定時刻ロックキーを配布し、ロック・タイムアウトを設定します。setNX(キー、値)の方法によってのみキーロックなどのトリガタイミングタスク、セクションにサービスのタスクは、現在のキーがキャッシュに存在し、真を返していない場合場合、を介して第2、(キーが期限切れ)タスク実行タイムアウトメソッドの後、ロック・タイムアウトを設定します。第2のサービス・タスクを入力するときは、タスク実行タイミング方法にジャンプしていない、ロックがキャッシュに存在することがわかっているときにロックを設定し、falseを返します。
コアコード
1.分散ロックセクション
@Aspect @ SLF4J @Component パブリッククラスCacheLockAspect { プライベート静的最終文字列LOCK_VALUE =「ロック」。 @Autowired プライベートRedisConnection接続。 @Around( "実行(* * *(..))&& @Annotation(com.common.annotation.CacheLock)。") 公共ボイドcacheLockPoint(ProceedingJoinPoint PJP){ 方法cacheMethod = NULL; 用(方法方法:。pjp.getTarget()のgetClass()getMethods()){ IF(!= NULL method.getAnnotation(CacheLock.class)){ cacheMethod =方法。 ブレーク; } } {試みる 文字列lockKey = cacheMethod.getAnnotation(CacheLock.class).lockedPrefix(); 長いタイムアウト= cacheMethod.getAnnotation(CacheLock.class).expireTime(); IF(ヌル== lockKey){ 新しいManagerException(ErrorMsgEnum.LOCK_NAME_EMPTY)を投げます。 } IF(connection.setNX(lockKey.getBytes()、LOCK_VALUE.getBytes())){ connection.expire(lockKey.getBytes()、TIMEOUT)。 log.info( "方法:{}获取锁:{}、开始运行"、cacheMethod、lockKey)。 pjp.proceed(); 返します。 } log.info( "方法:{}未获取锁:{}、运行失败"、cacheMethod、lockKey)。 }キャッチ(ThrowableをE){ log.error( "方法:{}!、运行错误"、cacheMethod、E)。 新しいManagerException(ErrorMsgEnum.LOCK_JOB_ERROR、e)を投げます。 } } }
2.手書きメソッドレベルの注釈
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented 公衆@interfaceのCacheLock { ストリングlockedPrefix()デフォルト""; //接頭辞キーロックRedisの 長いexpireTime(デフォルト10); //キーはRedisの中に存在します時間、1000S }
3.タイミングタスクサービス
@ SLF4J @Service パブリッククラスTimeTaskService { / ** *执行定时任务 ** / @Scheduled(cronを= "0 0 1 * *?") @CacheLock(lockedPrefix = "TimeTaskService"、expireTime = 30) 公共無効executeTask() { System.out.printlnは( "Hello Worldの!"); } }