源码分析ElasticJob 故障转移
原理描述:
ElasticJob设计核心理念:充分利用服务器资源执行任务,确保每个任务可以在多个节点上执行
ElasticJob失效转移过程:如果在任务执行过程中有一个执行实例挂了,那么之前被分配到这个实例的任务(或者分片)会在下次任务执行之前被重新分配到其他正常节点实例上执行
失效转移核心类如下:
FailoverService 作业失效转移服务
FailoverNode 作业失效转移数据存储路径。
FailoverListenerManager 作业失效转移监听管理器
失效转移监听管理器详解
class JobCrashedJobListener extends AbstractJobListener {
protected void dataChanged(final String path, final Type eventType, final String data) {
//判断1
if (isFailoverEnabled() && Type.NODE_REMOVED == eventType && instanceNode.isInstancePath(path)) {
String jobInstanceId = path.substring(instanceNode.getInstanceFullPath().length() + 1);
if (jobInstanceId.equals(JobRegistry.getInstance().getJobInstance(jobName).getJobInstanceId())) {
return;
}
List<Integer> failoverItems = failoverService.getFailoverItems(jobInstanceId);
if (!failoverItems.isEmpty()) {
for (int each : failoverItems) {
failoverService.setCrashedFailoverFlag(each);
failoverService.failoverIfNecessary();
}
} else {
for (int each : shardingService.getShardingItems(jobInstanceId)) {
failoverService.setCrashedFailoverFlag(each);
failoverService.failoverIfNecessary();
}
}
}
}
}
源码讲解:
1.如果配置中开启故障转移机制,监听/instances/{jobName}/leader 子节点删除事件,此时认为有节点宕机,执行故障转移方式
2.接着获取节点实例对象
3.此时删除任务节点id匹配当前实例节点id,如果一致忽略
4.获取当前失效转移分片集合,获取特定集合failoverService.getFailoverItems()实现如下
public void setCrashedFailoverFlag(final int item) {
if (!isFailoverAssigned(item)) { //创建节点 /{jobName}/leader/failover/items/{index}
jobNodeStorage.createJobNodeIfNeeded(FailoverNode.getItemsNode(item));
}
public List<Integer> getFailoverItems(final String jobInstanceId) {
List<String> items = jobNodeStorage.getJobNodeChildrenKeys(ShardingNode.ROOT);
List<Integer> 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;
}
继续分析:
1.创建节点 /{jobName}/leader/failover/items/{index}
2.获取/{jobName}/leader/sharding 子节点集合
3.判断failover节点是否存在,存在判断该分片是否为当前任务的分片节点
4.循环分片集合,查看是否执行失效转移failoverService.failoverIfNecessary() 实现如下
public void failoverIfNecessary() {
if (needFailover()) {//选举主节点
jobNodeStorage.executeInLeader(FailoverNode.LATCH, new FailoverLeaderExecutionCallback());
}
}
public void executeInLeader(final String latchNode, final LeaderExecutionCallback callback) {
try (LeaderLatch latch = new LeaderLatch(getClient(), jobNodePath.getFullPath(latchNode))) {
latch.start();
latch.await();
callback.execute();
//CHECKSTYLE:OFF
} catch (final Exception ex) {
//CHECKSTYLE:ON
handleException(ex);
}
}
private boolean needFailover() {
return jobNodeStorage.isJobNodeExisted(FailoverNode.ITEMS_ROOT) && !jobNodeStorage.getJobNodeChildrenKeys(FailoverNode.ITEMS_ROOT).isEmpty()
&& !JobRegistry.getInstance().isJobRunning(jobName);
}
}
1.失效转移过程中,需要判断是否需要失效转移,并判断是 否/{jobName}/leader/failover/items/{index}该节点存在
2.然后选举主节点 /{jobName}/leader/failover/latch( 分配失效转移分片项时占用的分布式锁为curator的分布式锁使用)
3.选举成功的主节点实例会在/{jobName}/leader/sharding/items/failover中设置当前实例instanceId
4.删除/{jobName}/leader/failover/items/分片项(新的实例接替失效转移分片)
5.回调过程中,存在多个分片情况,批量处理 job实例jobInstanceId绑定到分片参数路径下
5.获取分片信息,执行分片任务调度
作者简介:张程 技术研究
更多文章请关注微信公众号:zachary分解狮 (frankly0423)