03
Worker启动流程
3.1
WorkerServer启动
WorkerServer启动跟MasterServer启动一样,都是Springboot应用的,而且相比与Master而言会简单许多,因为只是执行task,不需要划分DAG等,只需要接收Netty请求执行而已。
WorkerServer的main函数启动之后,运行run方法,会干以下4个事情。
- 初始化NettyRemotingServer,这个跟MasterServer一样都会注册很多个Processor,Master和Worker的交互的Processor在第4章节说明。不过这里有个Processor值得着重强调一下,就是TaskExecuteProcessor,将在3.3章节中说明。
- WorkerRegistryClient向zk注册以及移除节点,原理完全跟MasterRegistryClient一样,也会开启一个心跳线程的,具体参考2.2章节内容的。
- WorkerManagerThread启动,从Worker ExecuteQueue中取出数据,TaskExecute Thread来执行task的,此队列如何插入数据参考3.3章节。
- RetryReportTasksStatsThread的启动,就是每5分钟将缓存里的Running和Result的信息发送给Master。
3.2
TaskPluginManager的installPlugin方法
首先说一下,TaskPluginManager是Service模块里的,并不是Worker模块下的。那为什么会在这里提这个类呢?理由很简单,就是加载task是使用了Java的SPI机制的。InstallPlugin是加载了task模块下的所有task放到内存中,Worker接收到Master发送的task信息,执行相对应的task。例如task的名字是SPARK,则执行SparkTask,task名字是FLINK,则执行FlinkTask。如果读者想扩展所需要的task信息,只需要参考下面链接即可。
https://mp.weixin.qq.com/s/nmbazSl-UW4SMivbS6E_6Q
3.3
TaskExecutorProcessor里执行Process方法
Master发送Command消息的在Worker模块一定会先经过这Process方法,至于为什么会经过这Process方法,将在第4.5章节说明。本节说这Process干了的4件事情。
- 校验并且转换成taskExecute RequestCommand,获得里面的tastExecutionContext。
- 检查操作用户是否存在以及获得执行的本地路径。
- 如果task需要延后时间,则(sendTaskExecuteDelayCommand)发送延后时间的请求方法给Master。
- 在WorkerManager中offer一条线程TaskExecuteThread。而TaskExecuteThread也是Worker的核心线程。
这个线程执行的内容将在3.4中说明。
3.4
TaskExecuteThread里执行run方法
在2.20中也有个TaskExecuteThread,不过这个是Master模块下,主要负责task的持久化操作,跟本节所说的Worker模块下的TaskExecuteThread没有关系,仅仅名字相同而已。
WorkerManagerThread中会构建一个固定的后台线程池,名字为Worker-Execute-Thread。里面共有100(WorkerConfig.getExecThreads)个线程。3.3中offer的线程就是到这个线程池里面的。线程Run方法主要干了下面7件事情:
- 检查用户或者租户是否存在。
- 改变运行状态,启动running状态,并且(sendTaskExecuteRunningCommand)发送运行状态给Master。
- 下载hdfs或者local文件(downloadResource)。
- 构建自定义参数(preBuildBusinessParam)。
- 通过taskPluginManager(spi机制)以及taskExecutionContext里的taskType获取相对应的taskChannel,并且根据taskChannel创建相对应的task。
- 初始化task->加载props->handle(执行具体task内容)->如果需要发送警告, 则发送alert。
- 最后,taskCallService发送响应给Master,并且清空task执行路径(clearTaskExecPath)。
下面以FlinkTask举例。在第6步中会加载FlinkTaskChannel,createTask创建出FlinkTask。在FlinkTask中,init方法会加载FlinkParams,SetMainJarHome以及SetMainArgs。在抽象类AbstrackYarnTask会执行handle方法(因为FlinkTask继承了AbstractYarnTask)。在handle方法中会BuildCommand 构建一条Shell命令的,由ShellCommandExecutor的父类Abstract Command Executor的run方法执行,封装TaskResponse并返回。具体如下图。
04
Master和Worker的交互流程
4.1
Netty简单介绍
第2章节和第3章节分别讲述了Master和Worker的启动过程的,本章节将讲述了Master和Worker如何通过Netty进行交互和处理消息的。Netty的demo就参考网上就好了。
本节只介绍使用Netty的server和client的API的相互交换过程的。里面具体的细节包括线程处理,网络传输等,可能需要参考专门的Netty资料的。
Netty的server和client的只考虑到的API的相互过程就会很简单。启动完Server和Client之后,Client会将传递的内容在固定的端口号下给WriteAndFlush方法。Server也在同样的端口号下ChannelRead读Client传递过来的内容的。读完之后也WriteAndFlush方法,Client也在ChannelRead读Server传递过来的内容。就这样如此反复。理解这个过程会对下面的流程很有帮助。如图所示:
4.2
Master 和 Worker 的 Cetty Remoting Server
在2.1节讲述MasterServer的时候有一类全是Processor的内容的,但在剩下的第二章节的内容都没有讲述这些Processor,因为这些Processor都是跟Worker进行交互作用的。在本章节讲述这些Processor的。
- 在MasterServer和WorkerServer都会启动NettyRemotingServer。
- 在MasterServer的NettyRemotingServer监听的端口号为5678,接收到Worker发送的信息,交给Master注册的Processor处理。
- 在WorkerServer的NettyRemotingServer监听的端口号为1234,接收到Master发送的消息,交给Worker注册的Processor处理。
至此Master和Worker服务端构建完成,接下来就是客户端的内容的。
4.3
Master下封装交互的Command
回到2.18节,在Master模块下,NettyExecutorManager的DoExecute方法中,有一行代码就是NettyRemotingClient.send(host,command)。在第二章节讲到这里就没讲了。因为从这里开始就是Master和Worker的交互过程了。本节将重点讲述Send方法参数command。
在2.16节TaskPriorityQueueConsumer干的第一件事情就是封装ExecutionContext,封装过程中有个ToCommand方法,会到这个Consumer类的ToCommand方法里,在ToCommand方法里中会进入到TaskExecuteRequestCommand类的Convert2Command方法里。而在这个Convert2Command里中构建Command,其中Command的Type为TASK_EXECUTE_REQUEST。这个Type极为重要,在交互过程中就是通过这个Type去找Processor的。
4.4
NettyRemotingClient里执行Send方法
在4.3节中讲了NettyRemotingClient的Command的参数,本节将讲述Send方法的。首先说明一下,NettyRemotingClient是Remote模块下的,换句话说Master和Worker都会用到这个Remote模块的。具体执行如下图所示。Connect就会连接Worker的IP和1234端口的。
4.5
NettyServerHandler里执行ChannelRead方法
如同4.1所说,NettyClient将Command数据writeAndFlush之后,NettyServer就会channelRead出来。注意本章节说的是
remote模块的org.apache.dolphinscheduler .remote.handler包下NettyServerHandler类。
不是remote模块的org.apache.dolphinscheduler .rpc.remote包下NettyServerHandler类
ChannelRead方法下面执行是ProcessRecieved方法。
在ProcessReceived方法下,会根据前面传过来的CommandType来获取相对应的NettyRequestProcessor。在4.3节中会根据TASK_EXECUTE_REQUEST去找到TaskExecutorProcessor 类。
执行TaskExecutorProcessor里面的Process方法,这就解释了3.3章节开头说的一定会经过TaskExecutorProcessor这个类的了。
4.6中有一张图详解Master和Worker的交互过程Master和Worker使用的Netty的内容都在remote模块,只是处理的模块不同,走的不同的是Processor而已。上面讲述了Master使用Netty的内容发送给Worker,而Worker发送消息给Master的话基本如出一辙。关于此,请读者自行研究了。
Master和Worker之间有很多Processor交互的,例如Master模块TaskKillResponseProcessor和taskEventProcessor等,Worker模块下的 HostUpdateProcessor以及TaskKillProcessor等,当看懂下面的这幅图的时候,这些Processor就不难理解的了。就请读者自行阅读的了。