elastic-job 源碼解讀之job配置數(shù)據(jù)存儲

? elastic-job作業(yè)相關(guān)的數(shù)據(jù)都是配置在zk上的履怯,包括分片參數(shù),作業(yè)失效轉(zhuǎn)移坷备,運(yùn)行實(shí)例等等都是保存在ZK上的殖演,那具體的zk節(jié)點(diǎn)的樹形結(jié)構(gòu)會是什么樣子氧秘?每一個(gè)節(jié)點(diǎn)又是什么時(shí)候注冊到zk上的?


Job.png

? 在job的啟動過程中(JobScheduler.init())趴久,會將啟動信息注冊到注冊中心丸相,再看一下具體的節(jié)點(diǎn)信息:

 public void init() {
        ///{jobName}/config路徑在這里
        LiteJobConfiguration liteJobConfigFromRegCenter =
          schedulerFacade.updateJobConfiguration(liteJobConfig); JobRegistry.getInstance().setCurrentShardingTotalCount(liteJobConfigFromRegCenter.getJobName(), liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getShardingTotalCount());
        JobScheduleController jobScheduleController = new JobScheduleController(
                createScheduler(), createJobDetail(liteJobConfigFromRegCenter.getTypeConfig().getJobClass()), liteJobConfigFromRegCenter.getJobName());
        JobRegistry.getInstance().registerJob(liteJobConfigFromRegCenter.getJobName(), jobScheduleController, regCenter);
        /** 
        /{jobName}/leader/election/latch
        /leader/election/instance
        /{jobName}/services/{ServerIp}
        /{jobName}/instances/{instanceIndex}    
        /{jobName}/sharding/necessary   
        **/
        schedulerFacade.registerStartUpInfo(!liteJobConfigFromRegCenter.isDisabled());
        jobScheduleController.scheduleJob(liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getCron());
}

持久化job的配置信息,首先將job的配置信息持久到zk節(jié)點(diǎn)上,看代碼:

LiteJobConfiguration liteJobConfigFromRegCenter=schedulerFacade.updateJobConfiguration(liteJobConfig);

   public LiteJobConfiguration updateJobConfiguration(final LiteJobConfiguration liteJobConfig) {
          configService.persist(liteJobConfig);//
          return configService.load(false);
   }

   public void persist(final LiteJobConfiguration liteJobConfig) {
        checkConflictJob(liteJobConfig);
        //configurationNode.ROOT=/{jobName}/config
        if (!jobNodeStorage.isJobNodeExisted(ConfigurationNode.ROOT) || liteJobConfig.isOverwrite()) {
            jobNodeStorage.replaceJobNode(ConfigurationNode.ROOT, LiteJobConfigurationGsonFactory.toJson(liteJobConfig));
        }
    }

  public void replaceJobNode(final String node, final Object value) {
  /** 
    節(jié)點(diǎn):
    /{jobName}/config
        在這里注冊
  **/
        regCenter.persist(jobNodePath.getFullPath(node), value.toString());
    }

在job啟動注冊啟動信息的時(shí)候朋鞍,會注冊很多信息,具體如下:

//JobScheduler.init();
schedulerFacade.registerStartUpInfo(!liteJobConfigFromRegCenter.isDisabled());

public void registerStartUpInfo(final boolean enabled) {
  listenerManager.startAllListeners();
  /** 
    節(jié)點(diǎn):
    /{jobName}/leader/election/latch
    /{jobName}/leader/election/instance
    在這里實(shí)現(xiàn)
  **/
  leaderService.electLeader();
  /**
  節(jié)點(diǎn):
    /{jobName}/servers/{ServerIp}   
     在這里創(chuàng)建
  **/
  serverService.persistOnline(enabled);
  /**
  節(jié)點(diǎn):
    /{jobName}/instances/{instanceId}   
  在這里創(chuàng)建
  **/
  instanceService.persistOnline();
  /**
  節(jié)點(diǎn):
    /{jobName}/sharding/necessary   
  在這里創(chuàng)建
  **/
  shardingService.setReshardingFlag();
  monitorService.listen();
  if (!reconcileService.isRunning()) {
    reconcileService.startAsync();
  }
}


public void electLeader() {
  log.debug("Elect a new leader now.");
  //
  //選舉主節(jié)點(diǎn) 在主節(jié)點(diǎn)下面創(chuàng)建節(jié)點(diǎn)LeaderNode.LATCH=/{jobName}/leader/election/latch
  jobNodeStorage.executeInLeader(LeaderNode.LATCH, new LeaderElectionExecutionCallback());
  log.debug("Leader election completed.");
}

public void executeInLeader(final String latchNode, final LeaderExecutionCallback callback) {
  //
  try (LeaderLatch latch = new LeaderLatch(getClient(), jobNodePath.getFullPath(latchNode))) {
    latch.start();
    latch.await();
    //回調(diào)妥箕,注冊主節(jié)點(diǎn)
    callback.execute();
    //CHECKSTYLE:OFF
  } catch (final Exception ex) {
    //CHECKSTYLE:ON
    handleException(ex);
  }
}

//在主節(jié)點(diǎn)選舉完成之后滥酥,執(zhí)行callBack
@RequiredArgsConstructor
class LeaderElectionExecutionCallback implements LeaderExecutionCallback {
  @Override
  public void execute() {
    if (!hasLeader()) {
      ///{jobName}/leader/election/instance 在這里
      jobNodeStorage.fillEphemeralJobNode(LeaderNode.INSTANCE, JobRegistry.getInstance().getJobInstance(jobName).getJobInstanceId());
    }
  }
}

再看一下執(zhí)行過程,最重要的一段獲取分片上下文畦幢,在獲取分片上下文的時(shí)候坎吻,首先會判斷是不是需要重新分片,需要分片的話宇葱,重新設(shè)置分片信息瘦真,在這里會做所有相關(guān)分片的邏輯。

//AbstractElasticJobExecutor 獲取上下文
ShardingContexts shardingContexts = jobFacade.getShardingContexts();

public ShardingContexts getShardingContexts() {
  boolean isFailover = configService.load(true).isFailover();
  if (isFailover) {
    List<Integer> failoverShardingItems = failoverService.getLocalFailoverItems();
    if (!failoverShardingItems.isEmpty()) {
      return executionContextService.getJobShardingContext(failoverShardingItems);
    }
  }
  //如果需要分片黍瞧,則重新分片
  shardingService.shardingIfNecessary();
  List<Integer> shardingItems = shardingService.getLocalShardingItems();
  if (isFailover) {
    shardingItems.removeAll(failoverService.getLocalTakeOffItems());
  }
  shardingItems.removeAll(executionService.getDisabledItems(shardingItems));
  return executionContextService.getJobShardingContext(shardingItems);
}   

//分片代碼
public void shardingIfNecessary() {
  List<JobInstance> availableJobInstances = instanceService.getAvailableJobInstances();
  if (!isNeedSharding() || availableJobInstances.isEmpty()) {
    return;
  }
  if (!leaderService.isLeaderUntilBlock()) {
    blockUntilShardingCompleted();
    return;
  }
  waitingOtherJobCompleted();
  LiteJobConfiguration liteJobConfig = configService.load(false);
  int shardingTotalCount = liteJobConfig.getTypeConfig().getCoreConfig().getShardingTotalCount();
  log.debug("Job '{}' sharding begin.", jobName);
  //分片之前诸尽,將zk節(jié)點(diǎn)狀態(tài)改為processing,分片中的狀態(tài)印颤,等待分片結(jié)束
  /**
  /{jobName}/sharding/processing
  **/
  jobNodeStorage.fillEphemeralJobNode(ShardingNode.PROCESSING, "");
  //重新設(shè)置分片項(xiàng)參數(shù)
  resetShardingInfo(shardingTotalCount);
  //獲取分片策略類
  JobShardingStrategy jobShardingStrategy = JobShardingStrategyFactory.getStrategy(liteJobConfig.getJobShardingStrategyClass());
  ///分片
  jobNodeStorage.executeInTransaction(new PersistShardingInfoTransactionExecutionCallback(jobShardingStrategy.sharding(availableJobInstances, jobName, shardingTotalCount)));
  log.debug("Job '{}' sharding complete.", jobName);
}

/**
    重新設(shè)子分片信息 
**/
private void resetShardingInfo(final int shardingTotalCount) {
  for (int i = 0; i < shardingTotalCount; i++) {
    /** 刪除jobInstance節(jié)點(diǎn)
        /{jobName}/sharing/{instanceIndex}分片項(xiàng)節(jié)點(diǎn)刪除
    **/
    jobNodeStorage.removeJobNodeIfExisted(ShardingNode.getInstanceNode(i));
    /** 刪除jobInstance節(jié)點(diǎn)
        /{jobName}/sharing/{instanceIndex}重新設(shè)置分片項(xiàng)
    **/
    jobNodeStorage.createJobNodeIfNeeded(ShardingNode.ROOT + "/" + i);
  }
  int actualShardingTotalCount = jobNodeStorage.getJobNodeChildrenKeys(ShardingNode.ROOT).size();
  if (actualShardingTotalCount > shardingTotalCount) {
    for (int i = shardingTotalCount; i < actualShardingTotalCount; i++) {
      //有多余分片刪除
      jobNodeStorage.removeJobNodeIfExisted(ShardingNode.ROOT + "/" + i);
    }
  }
}

/** 分片 **/
@RequiredArgsConstructor
class PersistShardingInfoTransactionExecutionCallback implements TransactionExecutionCallback {

  private final Map<JobInstance, List<Integer>> shardingResults;

  @Override
  public void execute(final CuratorTransactionFinal curatorTransactionFinal) throws Exception {
    for (Map.Entry<JobInstance, List<Integer>> entry : shardingResults.entrySet()) {
      for (int shardingItem : entry.getValue()) {
       /**
        每個(gè)分片項(xiàng)創(chuàng)建一個(gè)實(shí)例
        {jobName}/sharing/{instanceIndex}/
       **/
 curatorTransactionFinal.create().forPath(jobNodePath.getFullPath(ShardingNode.getInstanceNode(shardingItem)), entry.getKey().getJobInstanceId().getBytes()).and();
      }
    }
    /**
        刪除節(jié)點(diǎn)
        /{jobName}/sharding/necessary
        /{jobName}/sharding/processing
    **/    curatorTransactionFinal.delete().forPath(jobNodePath.getFullPath(ShardingNode.NECESSARY)).and(); curatorTransactionFinal.delete().forPath(jobNodePath.getFullPath(ShardingNode.PROCESSING)).and();
  }
}

在獲取分片上下文后您机,根據(jù)每個(gè)分片項(xiàng)判斷有無作業(yè)是運(yùn)行中的狀態(tài),如果有年局,則標(biāo)記為misfire

jobFacade.misfireIfRunning(shardingContexts.getShardingItemParameters().keySet())
  
public boolean misfireIfRunning(final Collection<Integer> shardingItems) {
  return executionService.misfireIfHasRunningItems(shardingItems);
}
    
/**
     * 如果當(dāng)前分片項(xiàng)仍在運(yùn)行則設(shè)置任務(wù)被錯過執(zhí)行的標(biāo)記.
     * 
     * @param items 需要設(shè)置錯過執(zhí)行的任務(wù)分片項(xiàng)
     * @return 是否錯過本次執(zhí)行
     */
public boolean misfireIfHasRunningItems(final Collection<Integer> items) {
  if (!hasRunningItems(items)) {
    return false;
  }
  setMisfire(items);
  return true;
}

  /**
     * 設(shè)置任務(wù)被錯過執(zhí)行的標(biāo)記.
     *
     * @param items 需要設(shè)置錯過執(zhí)行的任務(wù)分片項(xiàng)
     */
public void setMisfire(final Collection<Integer> items) {
  for (int each : items) {
    /**
    /{jobName}/{itemNum}/misfire
    **/
    jobNodeStorage.createJobNodeIfNeeded(ShardingNode.getMisfireNode(each));
  }
}

misfire判斷結(jié)束之后际看,回去執(zhí)行job,執(zhí)行開始時(shí)矢否,會將作業(yè)狀態(tài)改為running狀態(tài)仲闽,作業(yè)執(zhí)行完成,將running節(jié)點(diǎn)刪除僵朗。


private void execute(final ShardingContexts shardingContexts, final JobExecutionEvent.ExecutionSource executionSource) {
  if (shardingContexts.getShardingItemParameters().isEmpty()) {
    if (shardingContexts.isAllowSendJobEvent()) {
      jobFacade.postJobStatusTraceEvent(shardingContexts.getTaskId(), State.TASK_FINISHED, String.format("Sharding item for job '%s' is empty.", jobName));
    }
    return;
  }
  /**這里修改作業(yè)狀態(tài)
        {jobName}/{itemNum}/running
        **/
  jobFacade.registerJobBegin(shardingContexts);
  String taskId = shardingContexts.getTaskId();
  if (shardingContexts.isAllowSendJobEvent()) {
    jobFacade.postJobStatusTraceEvent(taskId, State.TASK_RUNNING, "");
  }
  try {
    // failOver邏輯在這里
    process(shardingContexts, executionSource);
  } finally {
    // TODO 考慮增加作業(yè)失敗的狀態(tài)赖欣,并且考慮如何處理作業(yè)失敗的整體回路
    // 刪除running節(jié)點(diǎn)
    //{jobName}/{itemNum}/running 
    jobFacade.registerJobCompleted(shardingContexts);
    if (itemErrorMessages.isEmpty()) {
      if (shardingContexts.isAllowSendJobEvent()) {
        jobFacade.postJobStatusTraceEvent(taskId, State.TASK_FINISHED, "");
      }
    } else {
      if (shardingContexts.isAllowSendJobEvent()) {
        jobFacade.postJobStatusTraceEvent(taskId, State.TASK_ERROR, itemErrorMessages.toString());
      }
    }
  }
}
/**
     * 注冊作業(yè)啟動信息.
     * 
     * @param shardingContexts 分片上下文
     */
public void registerJobBegin(final ShardingContexts shardingContexts) {
  JobRegistry.getInstance().setJobRunning(jobName, true);
  if (!configService.load(true).isMonitorExecution()) {
    return;
  }
  for (int each : shardingContexts.getShardingItemParameters().keySet()) {
    /**這里修改作業(yè)狀態(tài)
        {jobName}/{itemNum}/running
        **/
    jobNodeStorage.fillEphemeralJobNode(ShardingNode.getRunningNode(each), "");
  }
}

/**
     * 注冊作業(yè)完成信息.
     * 
     * @param shardingContexts 分片上下文
     */
public void registerJobCompleted(final ShardingContexts shardingContexts) {
  JobRegistry.getInstance().setJobRunning(jobName, false);
  if (!configService.load(true).isMonitorExecution()) {
    return;
  }
  for (int each : shardingContexts.getShardingItemParameters().keySet()) {
     /**在這里刪除節(jié)點(diǎn)
        {jobName}/{itemNum}/running
        **/
 jobNodeStorage.removeJobNodeIfExisted(ShardingNode.getRunningNode(each));
  }
}
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酱酬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌云矫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件让禀,死亡現(xiàn)場離奇詭異,居然都是意外死亡巡揍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門腮敌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糜工,你說我怎么就攤上這事弊添“颇荆” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵刨裆,是天一觀的道長。 經(jīng)常有香客問我帆啃,道長瞬女,這世上最難降的妖魔是什么努潘? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任拆魏,我火速辦了婚禮,結(jié)果婚禮上慈俯,老公的妹妹穿的比我還像新娘渤刃。我一直安慰自己,他們只是感情好贴膘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布卖子。 她就那樣靜靜地躺著,像睡著了一般刑峡。 火紅的嫁衣襯著肌膚如雪洋闽。 梳的紋絲不亂的頭發(fā)上玄柠,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音诫舅,去河邊找鬼羽利。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刊懈,可吹牛的內(nèi)容都是我干的这弧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼虚汛,長吁一口氣:“原來是場噩夢啊……” “哼匾浪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卷哩,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蛋辈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后将谊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冷溶,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年尊浓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逞频。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眠砾,死狀恐怖虏劲,靈堂內(nèi)的尸體忽然破棺而出托酸,到底是詐尸還是另有隱情褒颈,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布励堡,位于F島的核電站谷丸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏应结。R本人自食惡果不足惜刨疼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鹅龄。 院中可真熱鬧揩慕,春花似錦、人聲如沸扮休。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玷坠。三九已至劲藐,卻和暖如春樟凄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缝龄。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工二拐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人百新。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓饭望,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铅辞。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345