目录
Master提交执行
如图中start -> end, master提交执行大致就是从command开始,到提交到consumer结束。
MasterSchedulerService :
1. 负责消费command,并将command转化为processInstance,保存或更新DB并删除command,此时processInstance状态被更新为running状态。
2. 如果processInstance级别设置超时,添加到超时队列。
3. 创建WorkflowExecuteThread线程并首次提交,以后该线程的触发执行完全由事件驱动,该线程有且仅对应一个processInstance。
WorkflowExecuteThread :
1. 构建task dag图,用于触发提交下游
2. submitPostNode()方法,核心提交下游的方法,这里需要注意,在dag中,task是被上游触发提交的,而非自己检测上游判断自己是否达到提交条件,所以当依赖多个上游时,下游就有可能被重复提交。
1)内存构建taskInstance,这里没有落表
2)判断可提交task,会提交到 readyToSubmitTaskQueue 队列
3)触发readyToSubmitTaskQueue的执行,真正提交task,这里会通过判断上游task的状态来决定该task是否具备提交条件
TaskProcessor :
根据task的类型不同,抽象出来不同TaskProcessor处理器,用来管理task整个生命周期的所有操作,包括submit(),run(),kill()等,主要有两个操作
1. 将taskInstance保存DB,此时页面才看到一个task记录,状态为submit success
2. 构建TaskExecutionContext,再封装为TaskPriority,添加到taskPriorityQueue队列
3. 提交成功,会将task添加到task超时队列,task重试队列(见思考2),并且会将taskId到TaskProcessor映射缓存到activeTaskProcessorMaps,标识为激活的task,这个很重要,后面会参与process状态判断,触发task的其他操作等。
4. 从readyToSubmitTaskQueue移除task
TaskPriorityQueueConsumer :
本质是个轮询线程,不断扫描处理taskPriorityQueue队列的TaskPriority。
1. 根据不同策略选择worker进行提交。这里需要注意,worker选择策略只是优先选择出一个worker,不代表只会往这个worker上提交,具体提交策略是当一个worker提交失败后:会记录worker黑名单,从所有相同group的其他worker中,利用迭代器.next选择下一个worker继续提交,默认每个worker间隔100ms重试3次。
2. 收集提交失败的,放回队列
优化点1:
如果采用的LowerWeightHostManager,最好是同group的worker按负载排序,优先选低负载的worker。
优化点2:已修复
当worker没有资源时,频繁刷日志,这里高版本已经优化了,原理是提交失败后sleep。
日志:fail to execute : %s due to no suitable worker, current task needs worker group %s to execute
master注册的processor
这里重点说TaskAckProcessor和TaskResponseProcessor,当task提交到worker后,task在worker的执行情况主要靠这两个processor来接受了。并且会触发操作DB,更新task。最终会响应worker DBcommand。如图中两条粗红线所示。
TaskAckProcessor:
当worker接收到到task后,会给master响应ack,TaskAckProcessor接受到会封装ack event添加到TaskResponseService
TaskResponseProcessor:
当worker执行完task后,会给master响应result,TaskResponseProcessor接受到会封装result event添加到TaskResponseService
TaskResponseService:
1. 如果是ack会更新task的worker host,state为running状态,log路径等,并响应给worker的DBTaskAckProcessor
2. 如果是result会更新task的state(success或failed),endtime等,并响应给worker的DBTaskResponseProcessor
3. 封装TASK_STATE_CHANGE的event,添加到process线程,等待事件驱动执行
思考1:
任务如何按照优先级执行?
查看TaskPriority实现的compareTo()方法,大致是按照优先process级别,再task级别的优先级执行。
并且taskPriorityQueue队列是整个master服务级别的,添加进去的task是不区分租户和任务类型的,所以,有需求的同学需要二开TaskPriorityQueueConsumer的接受队列。
public int compareTo(TaskPriority other) {
if (this.getProcessInstancePriority() > other.getProcessInstancePriority()) {
return 1;
}
if (this.getProcessInstancePriority() < other.getProcessInstancePriority()) {
return -1;
}
if (this.getProcessInstanceId() > other.getProcessInstanceId()) {
return 1;
}
if (this.getProcessInstanceId() < other.getProcessInstanceId()) {
return -1;
}
if (this.getTaskInstancePriority() > other.getTaskInstancePriority()) {
return 1;
}
if (this.getTaskInstancePriority() < other.getTaskInstancePriority()) {
return -1;
}
if (this.getTaskId() > other.getTaskId()) {
return 1;
}
if (this.getTaskId() < other.getTaskId()) {
return -1;
}
return this.getGroupName().compareTo(other.getGroupName());
}
思考2:
task的retry是如何实现的?
首先理解下StateWheelExecuteThread: 一个轮询线程,不断的扫描重试队列,判断task是否满足重试(task重试条件是失败,到达重试时间且有重试次数),当达到重试条件,会用task生成一个TASK_STATE_CHANGE的event,并添加到对应process的WorkflowExecuteThread线程,之后就等待事件驱动执行
在TaskProcessor中,任务A1首次保存DB并提交到taskPriorityQueue队列后,就把A1添加到重试队列。当A1触发重试,会把A1提交到readyToSubmitTaskQueue,一直到保存DB时,才会判断task进行重置属性处理生成新的task A2,提交成功后,又将A2添加到重试队列,往复如此。
核心逻辑如下代码,1.task是失败状态,2.将A1设置flag = no,3.生成新的task A2,前提是processInstance状态不是RESDY_STOP和READY_PAUSE
由于重试间隔,在特定情况下,kill时有一个bug,会在kill逻辑中讲。
public TaskInstance submitTaskInstanceToDB(TaskInstance taskInstance, ProcessInstance processInstance) {
ExecutionStatus processInstanceState = processInstance.getState();
// 1. 判断task为失败
if (taskInstance.getState().typeIsFailure()) {
if (taskInstance.isSubProcess()) {
taskInstance.setRetryTimes(taskInstance.getRetryTimes() + 1);
} else {
if (processInstanceState != ExecutionStatus.READY_STOP
&& processInstanceState != ExecutionStatus.READY_PAUSE) {
// 2. 将重试task置为不可用 no
taskInstance.setFlag(Flag.NO);
updateTaskInstance(taskInstance);
// crate new task instance
if (taskInstance.getState() != ExecutionStatus.NEED_FAULT_TOLERANCE) {
taskInstance.setRetryTimes(taskInstance.getRetryTimes() + 1);
}
// 3. 重置属性,生成新的task
// 重置task一些属性,其余属性不变
taskInstance.setSubmitTime(null);
taskInstance.setLogPath(null);
taskInstance.setExecutePath(null);
taskInstance.setStartTime(null);
taskInstance.setEndTime(null);
taskInstance.setFlag(Flag.YES);
taskInstance.setHost(null);
// 设置id为0,保证新生成一条记录
taskInstance.setId(0);
}
}
}
......
}
可能的PR福利:
// master提交出去,收到响应,进行缓存
taskInstanceCacheManager.cacheTaskInstance(taskAckCommand);
TaskAckProcessor和TaskResponseProcessor都会在TaskInstanceCacheManager缓存taskInstance,但是没有找到释放的地方。