2021-12-13
2021SC@SDUSC
2021-12-13-DolphinScheduler(12)
worker的故障转移
/**
* failover worker tasks
*
* 1. kill yarn job if there are yarn jobs in tasks.
* 2. change task state from running to need failover.
* 3. failover all tasks when workerHost is null
* @param workerHost worker host
* @param needCheckWorkerAlive need check worker alive
* @throws Exception exception
*/
private void failoverWorker(String workerHost, boolean needCheckWorkerAlive) throws Exception {
logger.info("start worker[{}] failover ...", workerHost);
List<TaskInstance> needFailoverTaskInstanceList = processDao.queryNeedFailoverTaskInstances(workerHost);
for(TaskInstance taskInstance : needFailoverTaskInstanceList){
if(needCheckWorkerAlive){
if(!checkTaskInstanceNeedFailover(taskInstance)){
continue;
}
}
ProcessInstance instance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId());
if(instance!=null){
taskInstance.setProcessInstance(instance);
}
// only kill yarn job if exists , the local thread has exited
ProcessUtils.killYarnJob(taskInstance);
taskInstance.setState(ExecutionStatus.NEED_FAULT_TOLERANCE);
processDao.saveTaskInstance(taskInstance);
}
logger.info("end worker[{}] failover ...", workerHost);
}
首先根据worker的IP找到对应的任务实例,如果该实例不需要故障转移则继续下一个任务实例的检测;
如果需要,则找到对应的流程定义实例,将其与任务实例关联;
只去掉yarn作业,其他作业的本地线程已经退出;
设置任务实例的状态为ExecutionStatus.NEED_FAULT_TOLERANCE。
看到这里发现worker的故障转移就是设置任务实例的状态。
worker就是等对应的线程池安全退出才stop,其实就是让正在执行的任务实例继续执行完毕,也就是处理ExecutionStatus.NEED_FAULT_TOLERANCE状态的任务实例。
ExecutionStatus.NEED_FAULT_TOLERANCE
全局搜索ExecutionStatus.NEED_FAULT_TOLERANCE
if(task.getState()==ExcutionStatus.SUCCESS){
completeTasjKist.put(task.getName());
continue;
}
//node fails,retry first,and then execute the failure process
if(task.getState().typeIsFailure()){
if(task.getState()==ExecutionStatus.NEED_FAULT_TOLERANCE){
this.recoverToleranceFaultTaskList.add(task);
}
if(task.taskCanRetry()){
addTaskToStandByList(task);
}else{
//node failure, based on failure strategy
errorTaskList.put(task.getName(),task);
completeTaskList.put(task.gameName(),task);
if(processInstance.getFailureStrategy()==FailureStrategy.END){
killTheOtherTasks();
}
}
}
发现对ExecutionStatus.NEED_FAULT_TOLERANCE做过的唯一重要的判断是:
如果作业失败且需要故障转移,就把他放到recoverToleranceFaultTaskList列表中。
但跟踪代码才发现recoverToleranceFaultTaskList就是用来预警的。
/**
* determine if you can try again
* @return can try result
*/
public boolean taskCanRetry() {
if(this.isSubProcess()){
return false;
}
if(this.getState() == ExecutionStatus.NEED_FAULT_TOLERANCE){
return true;
}else {
return (this.getState().typeIsFailure()
&& this.getRetryTimes() < this.getMaxRetryTimes());
}
}
taskCanRetry中,如果是ExecutionStatus.NEED_FAULT_TOLERANCE状态,则不管重试了多少次,一定可以重试。就是说如果发现某个作业是故障转移状态,则失败的时候一定可以重试。
if(task.getState().typeIsFailure()){
if(task.getState() == ExecutionStatus.NEED_FAULT_TOLERANCE){
this.recoverToleranceFaultTaskList.add(task);
}
if(task.taskCanRetry()){
addTaskToStandByList(task);
}else{
// node failure, based on failure strategy
errorTaskList.put(task.getName(), task);
completeTaskList.put(task.getName(), task);
if(processInstance.getFailureStrategy() == FailureStrategy.END){
killTheOtherTasks();
}
}
continue;
}
上面是MasterExecThread.runProcess中的一段代码,其逻辑就是如果当前作业失败,且可以重试,就把作业添加到readyToSubmitTaskList队列中再次执行。而ExecutionStatus.NEED_FAULT_TOLERANCE就是属于可以重试。
因此DolphinScheduler故障转移的逻辑:
worker如果与zookeeper连接超时,则停止心跳,停止获取任务,等待所有任务实例执行结束(正常或失败)并更新数据库状态
master如果与zookeeper连接超时,则停止心跳,停止获取流程定义实例,停止调度所有流程定义实例
master如果发现某个流程定义实例中的任务实例失败且属于ExecutionStatus.NEED_FAULT_TOLERANCE状态,则重新运行。
假设master/worker与zookeeper的连接超时,则master/worker出现了问题,应该发生故障转移。
master/worker与zookeeper的连接超时有两种可能,
master/worker的网络有问题、zookeeper有问题。
如果是master/worker的网络有问题则MySQL的读写也会有问题,意味着任务实例的状态更新可能有问题,此时发生故障转移没问题;
如果是zookeeper服务本身有问题,则所有的master/worker可能都会有问题,即使发生故障转移意义不是特别大。
zookeeper存在的意义就是分布式锁,而不应是度量心跳,
DolphinScheduler把流程定义、实例等信息保存到了数据库,那么心跳应该去度量与数据库的连接,而不是去度量与zookeeper的连接。