源码分析ElasticJob故障失效转移机制

文章详细描述了ElasticJob中当监听到任务节点宕机时的故障失效转移过程,包括获取任务实例ID、判断分片转移、创建持久节点、执行转移逻辑以及故障分片的重新执行。重点介绍了如何通过监听和分布式锁实现任务在节点故障后的自动恢复。
摘要由CSDN通过智能技术生成

}

}

代码@1:如果配置文件中设置开启故障失效转移机制,监听到${namespace}/jobname/instances节点下子节点的删除事件时,则被认为有节点宕机,将执行故障失效转移相关逻辑。

代码@2:获取被宕机的任务实例ID(jobInstanceId)。

代码@3:如果被删除的任务节点ID与当前实例的ID相同,则忽略。

代码@4:根据jobInstanceId获取作业服务器的失效转移分片项集合。

其实现逻辑如下:FailoverService#getFailoverItems

/**

  • 获取作业服务器的失效转移分片项集合.

  • @param jobInstanceId 作业运行实例主键

  • @return 作业失效转移的分片项集合

*/

public List getFailoverItems(final String jobInstanceId) {

List items = jobNodeStorage.getJobNodeChildrenKeys(ShardingNode.ROOT);

List result = new ArrayList<>(items.size());

for (String each : items) {

int item = Integer.parseInt(each);

String node = FailoverNode.getExecutionFailoverNode(item);

if (jobNodeStorage.isJobNodeExisted(node) && jobInstanceId.equals(jobNodeStorage.getJobNodeDataDirectly(node))) {

result.add(item);

}

}

Collections.sort(result);

return result;

}

首先获取 n a m e s p a c e / j o b n a m e / s h a r d i n g 目录下的直接子节点 ( 当前的分片信息 ) ,判断 {namespace}/jobname/sharding目录下的直接子节点(当前的分片信息),判断 namespace/jobname/sharding目录下的直接子节点(当前的分片信息),判断{namespace}/jobname/sharding/{item}/failover节点是否存在,如果存在判断该分片是否为当前任务的分片节点,如果是,则返回。该方法的主要目的就是获取已经转移到当前任务节点的分片信息。

代码@5,判断是否有失败分片转移到当前节点,初始状态肯定为空,将执行代码@6,设置故障转移相关准备环境。

代码@6,获取分配给Crashed(宕机的job实例)的所有分片节点,遍历已发生故障的分片,将这些分片设置为故障,待故障转移,设置为故障的实现方法为:创建${namespace}/jobname/leader/failover/items/{item}。

代码@7:执行FailoverService#failoverIfNecessary是否执行故障转移。

/**

  • 如果需要失效转移, 则执行作业失效转移.

*/

public void failoverIfNecessary() {

if (needFailover()) {

jobNodeStorage.executeInLeader(FailoverNode.LATCH, new FailoverLeaderExecutionCallback());

}

}

private boolean needFailover() {

return jobNodeStorage.isJobNodeExisted(FailoverNode.ITEMS_ROOT) && !jobNodeStorage.getJobNodeChildrenKeys(FailoverNode.ITEMS_ROOT).isEmpty()

&& !JobRegistry.getInstance().isJobRunning(jobName);

}

其实现思路:【needFailover方法】首先判断是否存在 n a m e s p a c e / j o b n a m e / l e a d e r / f a i l o v e r / i t e m s 节点是否存在,并且其节点下是否有子节点,并且节点也运行该任务,则需要执行故障失效转移。执行失效转移的逻辑也是进行失效转移选主,其分布式锁节点为: {namespace}/jobname/leader/failover/items节点是否存在,并且其节点下是否有子节点,并且节点也运行该任务,则需要执行故障失效转移。执行失效转移的逻辑也是进行失效转移选主,其分布式锁节点为: namespace/jobname/leader/failover/items节点是否存在,并且其节点下是否有子节点,并且节点也运行该任务,则需要执行故障失效转移。执行失效转移的逻辑也是进行失效转移选主,其分布式锁节点为:{namespace}/jobname/leader/failover/latch,谁先获得锁,则执行失效故障转移具体逻辑(FailoverLeaderExecutionCallback),具体的失效转移算法为:

FailoverService#FailoverLeaderExecutionCallback:

class FailoverLeaderExecutionCallback implements LeaderExecutionCallback {

@Override

public void execute() {

if (JobRegistry.getInstance().isShutdown(jobName) || !needFailover()) { // @1

return;

}

int crashedItem = Integer.parseInt(jobNodeStorage.getJobNodeChildrenKeys(FailoverNode.ITEMS_ROOT).get(0)); // @2

log.debug(“Failover job ‘{}’ begin, crashed item ‘{}’”, jobName, crashedItem);

jobNodeStorage.fillEphemeralJobNode(FailoverNode.getExecutionFailoverNode(crashedItem), JobRegistry.getInstance().getJobInstance(jobName).getJobInstanceId()); // @3

jobNodeStorage.removeJobNodeIfExisted(FailoverNode.getItemsNode(crashedItem)); // @4

// TODO 不应使用triggerJob, 而是使用executor统一调度

JobScheduleController jobScheduleController = JobRegistry.getInstance().getJobScheduleController(jobName); // @5

if (null != jobScheduleController) {

jobScheduleController.triggerJob();

}

}

}

代码@1:如果当前实例停止运行该job或无需执行失效故障转移,则返回。

代码@2:获取第一个待故障转移的分片,获取${namespace}/jobname/leader/failover/items/{itemnum,获取分片序号itemnum。

代码@3:创建临时节点${namespace}/jobname/sharding/itemnum/failover节点。

代码@4:删除${namespace}/jobname/leader/failover/items/{itemnum}节点。

代码@5:触发任务调度,并结束当前节点的故障失效转移,然后释放锁,下一个节点获取锁,进行转移${namespace}/jobname/leader/failover/items目录下的失效分片。

PS:故障实现转移基本实现思路为:当一个任务节点宕机后,其他节点会监听到实例删除事件,从实例目录中获取其实例ID,并从ZK中获取原先分配故障实例的分片信息,并将这些分片标记为需要故障转移(创建${namespace}/jobname/leader/failover/items/{item}持久节点),然后判断是否需要执行故障转移操作。

执行故障转移操作的前提条件是:

  1. 当前任务实例也调度该job;

  2. 存在 n a m e s p a c e / j o b n a m e / l e a d e r / f a i l o v e r / i t e m s 节点并有子节点。如果满足上述两个条件,则执行失效转移,多个存活节点进行选主 ( L e a d e r L a t c h ) , 创建分布式锁节点 ( {namespace}/jobname/leader/failover/items节点并有子节点。如果满足上述两个条件,则执行失效转移,多个存活节点进行选主(LeaderLatch),创建分布式锁节点( namespace/jobname/leader/failover/items节点并有子节点。如果满足上述两个条件,则执行失效转移,多个存活节点进行选主(LeaderLatch),创建分布式锁节点({namespace}/jobname/leader/failover/latch),获取锁的节点优先执行获取分片节点,其具体过程如上述所示,每个存活节点一次故障转移只竞争一个分片。

2、故障分片重新执行逻辑分析


上述事件监听器主要的作用是当任务节点失效后,其他存活节点“瓜分”失效节点的分片,创建${namespace}/jobname/sharding/{item}/failover节点。但这些分片的任务并没有真正执行,本小结将梳理故障节点分片的执行。

可以看得出来,分片故障转移,就是在对应的故障分片下创建了failover节点,在获取分片信息上下文时会优先处理,这也是在分析分片流程时并未重点讲解的。因此,在进入下述内容之前,请先阅读 源码分析ElasticJob的分片机制。

回到定时任务调度执行入口:AbstractElasticJobExecutor#execute

/**

  • 执行作业.

*/

public final void execute() {

try {

jobFacade.checkJobExecutionEnvironment();

} catch (final JobExecutionEnvironmentException cause) {

jobExceptionHandler.handleException(jobName, cause);

}

ShardingContexts shardingContexts = jobFacade.getShardingContexts(); // 获取分片上下文环境

}

LiteJobFacade#getShardingContexts

技术学习总结

学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。

最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

4205257624)]

[外链图片转存中…(img-K2WKFGIo-1714205257625)]

[外链图片转存中…(img-Jz5Br06c-1714205257625)]

最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

[外链图片转存中…(img-RDMBaajW-1714205257625)]

[外链图片转存中…(img-D2ZQn4Lz-1714205257625)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值