Javaはredisを通じてクラスタのタイミングタスクを管理します

3554 ワード

前言
実習の時、筆者はspring-taskとquartzのフレームワークに接触し、javaプログラマーはいずれもタイミングタスクの需要があり、単機でタイミングタスクを使用するのは簡単だが、クラスタ環境では難しいと信じている.
  • は、1台のマシンだけがタイミングタスクを実行する
  • をどのように限定するか.
  • あるサービスがダウンタイムした後、どのようにフェイルオーバを行うか
  • 実行中のサービスをどのように決定するか
  • この問題には公式に解決策があるに違いない.資料も少なくない.quartzクラスタ分布式(同時)配置ソリューション-Springは筆者は怠け者だが、上述のソリューションには多くのquartzテーブルを配置する必要があり、業務にはタイミングタスクを動的に配置する必要はなく、quartzフレームワークに依存していない.したがって,他の方法を用いてタイミングタスクの分散スケジューリングを実現することを考慮する.
    Uncode-Scheduleの初期試行
    Uncode-Scheduleは筆者がオープンソース中国で見たオープンソースの分散スケジューリングタスクコンポーネントであり、springとの統合が比較的容易で、使用が便利で、zookeeperを借りている.ここで私はあまり評価しません.結局、個人のオープンソースフレームワークなので、使用中にいくつかの穴を踏んだのか、ドキュメントがまだそろっていないので、しばらくしてから廃棄することにしました.興味のある学生は以下のことを理解することができます:分布式タスクスケジューリングコンポーネントUncode-Scheduleは幸いにも穴を埋める過程でこのタスクスケジューリングの過程に対して一定の理解があって、そこで自分で簡単なタスクスケジューラを実現することを決定しました.
    redisによるタスクスケジューリングの考え方
    機能を実装する前に、シングルポイント実行、フェイルオーバ、サービスステータスの3つの問題をレビューします.redisのいくつかのインタフェースの特性を結びつけて、解決の構想は以下の通りです:
  • タスクスケジューリングセンタとしてredisを用いる、redisの自動期限切れと分散ロック特性
  • を採用した.
  • 各サービスのipプラスプロジェクト名は、各サービスの一意の別名
  • として使用される.
  • は、redisのkey値に対応するvalueによって、どのサービスが実行されるかを判定する.例えば、redisのkeyがschedular_であるroot:projectA,valueは192.168.1.187. プロジェクトプロジェクトAが現在実行するタスクを意味するノードは192.168である.1.187この機械のサービス
  • タスクを実行するたびにredisでschedular_を判定するroot:projectAが空かどうか、空であれば現在のipを設定し、一定時間の有効期間を設定し、タイミングタスクを実行します.空でない場合は、自機ipと同一か否かを判断し、同一である場合はタイミングタスクを実行し、そうでない場合は
  • をスキップする.
  • 有効期間を設けるのは、ある機器が故障した場合にフェイルオーバできるようにするための
  • である.
    コアプロセスコード
    このソリューションは非常に簡単で,コアコードも集積しやすく,結合度を低減するためにspringのaopを用いて実現した.
    コアAOPコード
    @Aspect
    @Component
    @Log4j
    public class QuartzAop {
        
        public boolean checkStatus(){
            String key = "schedular_root:projectA";
            try {
                //           ,       
                while (true) {
                    //        
                    boolean lock = RedisUtil.checkLock(key,1);
                    if (lock) {
                        //     ,    
                        break;
                    }
                }
                String ip = InetAddress.getLocalHost().getHostAddress();
                //          ip
                String currentIp = RedisUtil.get(key);
                //        ,    
                if(currentIp == null){
                    RedisUtil.setex(key, ip, 10);
                    return true;
                }
                //       ,   true
                if(currentIp.equals(ip)){
                    return true;
                }else{
                    return false;
                }
            } catch (Exception e) {
                log.error(e);
                return false;
            } finally {
                RedisUtil.unLock(key);
            }
        }
        
        @Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
        public void around(ProceedingJoinPoint jp) throws Throwable{
            if(checkStatus()){
                String ip = InetAddress.getLocalHost().getHostAddress();
                log.info("      "+jp.getSignature()+":"+ip);
                jp.proceed();
            }
        }
        
    }
    

    RedisUtilのロックコード
    public static boolean checkLock(String key,int second) {
      
      String lockKey = "lock:" + key;
      try {
       // 1       ,    
       if (setnx(lockKey, "lock") == 1) {
        //      
        setExpiredTime(lockKey, second);
        return true;
       } else {
        // 50     ,      
        try {
         Thread.sleep(50L);
        } catch (InterruptedException e) {
         log.error(e);
        }
        return false;
       }
    
      } catch (RedisException e) {
       log.error(e);
       return true;
      }
     }
    

    コアコードはすでに全放送されており、redisについては分散ロックを実現し、別の時間に皆さんと共有しますので、ご注目ください.