达人专栏|还不会用海豚调度吗,大佬用一个月写出最全入门教学三 原创
作者 | 欧阳涛 招联金融大数据开发工程师
02 Master启动流程
2.10 WorkFlowExecutorThread 里执行 Submit StandByTask 方法
SubmitStandByTask干了5件事情:
- 从ReadyToSubmitTaskQueue中取出TaskInstance。
- (这个TaskInstance是可以重试并且设定为强制成功了的)把task放到completeTaskMap以及taskInstanceMap,并从队列中移除。
- 如果这个task是首次执行的话,就会先从task和ProcessInstance中获取参数(varPool)【这一步的方法是GetPreVarPool】
- 获取这个task依赖结果【这一步的方法是GetDependResultForTask】
- 根据第4步获取的依赖结果,如果依赖结果为失败或者不执行,就从队列中移除,并且放到FailedTaskMap里的。如果依赖结果为成功则将执行SubmitTaskExec方法,同时会放到CompleteTaskMap。至于SubmitTaskExec做了哪些事情将在2.11中说明。
2.11 WorkFlowExecutorThread里执行SubmitTaskExec方法
SumbitTaskExec干了9件事情:
- PackageTaskInstance封装了TaskInstance,就是将TaskInstance和ProcessInstance进行了绑定,并且获取到了MainJar,ResourceList这些信息。
- 根据TaskType获取CommonTaskProcessor,这里采用SPI机制获取。如果想具体了解SPI机制的,可以百度搜索AutoService注解以及ServiceLoad进行详细了解。
- CommonTaskProcessor初始化,也就是将TaskInstance、ProcessInstance、ProcessService、MasterConfig传递给CommonTaskProcessor。
- 通知流程所在的主机,通过netty发送Host和HostUpdateCommand。
- 将CommonTaskProcessor的Action为submit(提交)状态。(这步极为重要)
- 放入到ValidMap,TaskInstanceMap,ActiveTaskProcessorMaps里。
- 将CommonTaskProcessor的Action设置成Run状态的。
- 将task以及ProcessInstance放入到StateWheelExecuteThread进行checkout。
- 如果这个task执行完成就添加到StateEvents队列中。
下一节讲述commonTaskProcessor的submit状态。
2.12 CommonTaskProcessor里执行Submit Task方法
回顾一下上节的第5步,CommonTaskProcessor的Action设置为Submit之后,
去ComonTaskProcessor的父类BaseTaskProcessor找Action方法,在Action方法中有个Switch结构,很明显会进入Submit方法,之后就进入本节所说的SubmitTask方法的了。
SubmitTask在这里干了三件事情:
- ProcessService。SubmitTaskWithRetry可以重复5次(MasterConfig.GetTask CommitInterval)提交task任务,最后在ProcessServiceImpl执行submitTask。
- 将此task的信息插入到TaskGroupQueue数据表中。
- DispatchTask下发任务,将Task任务下发到实现了TaskPriorityQueue接口的TaskPriorityQueueImpl中去。
在ProcessServiceImpl如何执行submitTask将在2.13中说明,同时DispatchTask下发做了那些事情,将在2.15中说明。
2.13 ProcessServiceImpl里执行SubmitTask方法
ProcessServiceImpl是属于Service模块的,SubmitTask主要干了2件事情:
- SubmitTaskInstanceToDB 将任务实例保存到数据库中,当然这里面有数据结构(TaskInstance)的变化,纯属业务的改变的。
- 如果此非结束状态,CreateSubWorkerProcess创建子流程,如果没有子流程,直接跳过2.14的内容。进入2.15。创建子流程做了哪些事情将在2.14中说明。
2.14 ProcessServiceImpl里执行CreateSubWork Process方法
创建子流程需要干6件事情:
- FindWorkProcessMapByParent查找父流程与task绑定的ProcessInstanceMap,是流程实例与Task关系的表。
- SetProcessInstanceMap。设置刚刚查找的ProcessInstanceMap,如果能找到以前跑的ProcessInstanceMap,更新这个ProcessInstanceMap,如果没有找到就创建新的ProcessInstanceMap,并插入到数据库中。
- CreateSubProcessCommand,根据参数,父流程等创建子流程命令的(SubProcessCommand)。
- UpdateSubProcessDefinitionByParent根据父流程更新子流程的定义。
- InitSubInstanceState初始化子实例状态。
- CreateCommand将创建的子流程命令插入数据库中。
这里ProcessInstanceMap并不是jdk包下的map,而是表t_ds_relation_process_instance的数据的。里面存储了父流程实例以及任务的关系的。3到6这些步骤都是crud的业务,里面具体的细节就赘述了。
2.15 CommonTaskProcessor里执行Dispatch Task方法
DispatchTask方法干了三件事情:
- 获取TaskPriorityQueueImpl的bean。
- 将TaskInstance,ProcessInstance封装成TaskPriority。
- 将封装后的TaskPriority放到这bean下的queue中去,这个队列是jdk的PriorityBlockingQueue,是一个具有优先级别的无界阻塞队列。
此时将DispatchTask放进task,那如何消费队列中的task的呢?2.16将说明这个议题。
2.16 TaskPriorityQueueConsumer执行run和dispatchTask方法
TaskPriorityQueueConsumer是一个继承Thread的类。在MasterServer启动之后,根据Spring的特性,TaskPriorityQueueConsumer会创建一个对象由Spring管理。TaskPriority会执行init的方法。线程启动并且设置线程名字Task UpdateConsumerThread。
Run方法中以3(MasterConfig.getMasterDispatch Task ) 次拉取为循环,每次1秒从队列中(BatchDispatch)拉取TaskPriority,如果失败就有重新丢回到这队列中去。
随后对拉取的数据进行DispatchTask方法。
DispatchTask方法中做了三件事情:
- 从TaskPriority中取出context,根据Command,ExecutorType和Workergroup封装成Execution Context。
- 将ExecutionContext交给Executor Dispatcher进行Dispatcher,这将在2.17中说明。
- 如果发送成功,返回result为true。将TaskEvent添加到TaskEventService (addEvents)中,由TaskEventService进行管理的。TaskEventService的说明将在2.19中介绍。
2.17 ExecutorDispatcher里执行Dispatch方法
ExecutorDispatcher这个类就干了三件事情:
- ExecutorDispatcher此类实现了InitializingBean。也就是创建过程中执行了AfterPropertiesSet方法,ExecutorManagers注册了Worker和Client的ExecutorType。
- Dispatch方法中获取到了Worker的ExecutorType,然后进行HostManagar.select。在Select方法中会根据MasterConfig中的Host-selector策略选择机器,默认是Lower-weight。如果读者有自定义的需求,则可以实现HostManager接口的。(Lower-weight如何选择的,就不详细介绍了。因为难度并不大,也就是纯属业务的变化的,有兴趣就可以自行阅读的。)
- 选择完了Host之后,调用ExecutorManager进行execute。这里的EeforeExecute和AfterExecute是没有内容的,如果读者有需求,同样可以在此添加内容的。在2.18中会说明execute的内容。
2.18 NettyExecutorManager执行execute和doExecute方法
ExecutorManager目前就一个实现类,就是NettyExecutorManger。
在init方法中NettyRemotingClient注册了TaskExecuteResponse、TaskExecuteAck和
TaskKillResponse的Processor。这些Processor是用来让Master和Worker进行交互的。
在Executor方法中最核心的方法就是DoExecute。
在DoExecute中NettyRemotingClient根据有效的Host发送Command。如果发送失败了,剔除失败节点,将task重新添加到队列中。
至此,Master就以Command形式发送task信息给Worker,说明一下,此时的Command是Remote包下的Command,与前面的Command没有任何关系的,不要混淆了。Master和Worker的交互过程会在第四章节中讲述。
2.19 TaskEventService执行addEvents方法
先说说TaskEventService创建过程。这是由Spring管理的,然后执行Start方法之后,有两个线程创建出来,一个是TaskEventThread,另外一个是TaskEventHandlerThread。在TaskEventThread会从EventQueue中取出TaskEvent事件进行提交(submitTaskEvent)。而TaskEventHandlerThread会执行EventHandler方法。EventHandler中会从TaskExecuteThreadMap中取出数据来执行executeEvent方法。
那么TaskExecuteThreadMap如何插入数据的呢?答案就是本节所说的addEvents方法。
addEvents方法中会调用TaskExecuteThreadPool中的SubmitTaskEvent方法。而在SubmitTaskEvent方法中最核心的功能就是往TaskExecuteThreadMap放入数据,也就是以ProcessInstanceId为key,TaskExecuteThread为value的map,并且会调用TaskExecuteThrad的addEvent方法,将event放入到events队列中。
至于TaskExecuteThread做了哪些事情将在2.20中说明。
2.20 TaskExecuteThread执行Persist方法
接上文2.19的在TaskExecuteThreadPool中ExecuteEvent方法。
执行TaskExecuteThread中的run方法。在run方法中从events队列中取出TaskEvent,并执行Persist持久化操作的,将task信息保存到数据库中的。
在Persist方法中,重点是Switch结构下的内容。根据DISPATCH,RUNNING,RESULT,执行不同的方法,封装不同的TaskInstance内容保存到数据库中,并发送请求给Worker。
另外构建StateEvent对象,交给WorkerflowExecuteThreadPool进行处理持久化后的StateEvent对象。stateEvent应该如何处理呢?请参考2.22的内容。
2.21 MasterSchedulerService总结
MasterServer的MasterSchedulerService已经基本讲完。回到最开始的MasterServer这部分,发现MasterSchedulerService后面的两个bean没有讲,也就是EventExecuteService以及FailoverExecute Thread.这两个都是线程的,将在2.22和2.23中说明这最后两个bean。
2.22 EventExecuteService线程的run方法
在MasterServer调用Start方法后,EventExeuctor Service的run方法执行过程如下:
- 每100毫秒执行EventHandler方法。
- 每次执行EventHandler方法时,从2.5章节的第3步ProcessInstance ExecCacheManager中取出WorkFlowExecutorThread,通过WorkflowExecuteThreadPool执行ExecuteEvent方法。
- 在ExecuteEvent方法中, 可以发现最核心的方法就是HandlerEvents方法。
- 在HandlerEvents中可以发现,从2.11章节的第9步的StateEvents队列取出StateEvent,然后在通过StateEventHandler方法进行判断的。
- 在WorkflowExecutorThread的stateEventHandler方法中,根据StateEventType的不同,以有6种不同类型的方法去调用,分别为PROCESS_STATE _CHANGE、TASK_STATE_CHANGE、PROCESS_TIMEOUT、TASK_TIMEOUT、TASK_RETRY、PROCESS_BLOCKED。通过不同的type调用不同的方法,如PROCESS_STATE_CHANGE调用ProcessStateChangeHandler方法,这里就不详细讲述各个方法的内容了,其本质上也都是内存数据结构的变化。
P.S.:
- 如果StateEventHandler方法中某一类型成功执行,则从StateEvents队列中移除它了。
- 返回到WorkflowExecuteThreadPool类的ExecuteEvent方法中,执行完第3步之后,会有个回调函数,失败就执行OnFailure方法。成功就执行OnSuccess方法,NotifyProcessChanged通知流程改变中,要么NotifyMyself,要么通知其他流程NotifyProcessChanged的。
2.23 FailoverExecutorThread线程的Run方法
此节为机器故障切换执行的线程,主要干了5件事情。具体执行流程如下:
- Run方法中FailoverService.checkMaster Failover检查是否需要切换的host。
- 如果有host的话,就进入FailoveMaster WithLock方法。在此方法中,从zk中通过分布式锁来进行切换机器,也就是进入FailoverMaster方法。
- 在FailoverMaster中,从ProcessSerivce里(QueryNeedFailover ProcessInstance)查询所需要切换的流程实例(NeedFailover ProcessInstanceList)。
- 接下来,就是通过zk获取有效的WorkerServers.failoverTaskInstance来切换task。在切换task时有三个步骤,分别是:当是Yarnjobs时,则直接杀掉 ; 改变task的状态,也就是从Running到Needfailover ; WorkflowExecutor ThreadPool提交StateEvent。
- 在ProcessService中处理该切换的流程,增加切换流程实例的Command,插入数据库中。
下两章将继续讲述Worker和Master与Worker的交互。