源碼分析ElasticJob 失效轉移

源碼分析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)
my

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章