一、本地开发环境的搭建
1、准备工作:
1.1、环境:
后端
- JDK(1.8+)
- Maven(3.3+)
最好在本地解压一个hadoop并配上环境变量(不配好像会报一个winutils…的问题)
前端
- node(Node包下载 (注意版本 8.9.4) https://nodejs.org/download/release/v8.9.4/)
本地环境变量的配置:
1.2、源码下载
git branch -a
#查看分支git checkout dev-db
#切换到dev-db分支git pull
#同步分支
#由于项目前端的日志模块,使用了gRPC调用后端,所以需要先编译项目。- 编译项目:
mvn -U clean package -Prelease -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
(1.2之后的版本)
(1.2之前:mvn -U clean package assembly:assembly -Dmaven.test.skip=true
)
*如果dolpinscheduler-ui模块编译不过,可以直接去pom.xml文件中去掉ui模块,跳过编译
2、搭建后端:
2.1、修改 pom.xml 文件 [采用 mysql 数据库 ]
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
<!--<scope>test</scope>-->
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
2.2、修改配置
1、dolphinscheduler-common的hadoop.properties
主要修改的配置项有:
fs.defaultFS=hdfs://xxxx
yarn.resourcemanager.ha.rm.ids=192.168.0.244,192.168.0.245
- 1
- 2
2、dolphinscheduler-common的common.properties
主要修改的配置项有:
res.upload.startup.type=HDFS
- 1
3、dolphinscheduler-common的quartz.properties
:ds1.2数据库默认使用postgresql,所以如果使用mysql,需要修改mysql的配置
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.dataSource.myDs.driver = com.mysql.jdbc.Driver
- 1
- 2
4、dolphinscheduler-common的zookeeper.properties
:配置连接及路径
5、dolphinscheduler-dao的application-dao.properties
修改spring.datasource的相关配置
2.3、数据库初始化
- 创建ds数据库
CREATE DATABASE dolphinscheduler
- 创建表和初始化数据: 修改dao模块resource目录下
application.properties
文件中的数据库配置信息,然后执行org.apache.dolphinscheduler.dao.upgrade.shell.CreateDolphinScheduler
的类,运行完,刷新数据库,表和数据都有了。
2.4、运行前的配置修改
2.4.1、启动MasterServer
org.apache.dolphinscheduler.server.master.MasterServer类main函数增加如下代码:
System.setProperty("spring.profiles.active","master");
- 1
修改server模块resources目录下master_logback.xml文件,增加以下代码:
<root level="INFO">
<appender-ref ref="MASTERLOGFILE"/>
<!-- 增加日志到控制台-->
<appender-ref ref="STDOUT"/>
</root>
- 1
- 2
- 3
- 4
- 5
然后执行MasterServer即可。
2.4.2、启动WorkerServer
org.apache.dolphinscheduler.server.worker.WorkerServer类main函数增加如下代码:
System.setProperty("spring.profiles.active","worker");
- 1
修改server模块resources目录下worker_logback.xml文件,增加以下代码:
<root level="INFO">
<appender-ref ref="TASKLOGFILE"/>
<appender-ref ref="WORKERLOGFILE"/>
<!-- 增加日志到控制台-->
<appender-ref ref="STDOUT"/>
</root>
- 1
- 2
- 3
- 4
- 5
- 6
然后执行WorkerServer即可。
2.4.3、启动CombinedApplicationServer的mian函数下增加
System.setProperty("spring.profiles.active","combined");
- 1
2.4.4、错误收集:
1、如果报找不到/etc/passwd文件的错误,则定位到指定位置OSUtils.java,从服务器上下载一个passwd文件到本地,修改路径
2、如果无法获取fs文件系统,是因为之前common模块下的hadoop.properties中是配置的
fs.defaultFS=hdfs://xxxx 命名空间,会显示无法解析xxxx,所以将core-site.xml和hdfs-site.xml拷贝到common的resource模块下。
3、搭建前端
3.1、编译
cd apache-dolphinscheduler-1.2.1-src/dolphinscheduler-ui
npm install node-sass --unsafe-perm
(不一定每次都会安装成功,看人品吧)
npm install
[ 慢的话用这个 npm install --registry https://registry.npm.taobao.org
]
3.2、修改配置
/workspace/apache-dolphinscheduler-1.2.1-src/dolphinscheduler-ui/.env
下的
API_BASE = http://127.0.0.1:12345
DEV_HOST = 127.0.0.1
3.3启动
npm run dev
4、观看效果:
地址:127.0.0.1:8888
用户名\密码:admin\dolphinscheduler123
二、源码结构分析
1、DolphinScheduler的架构图
架构说明:
1.1、Quartz
SchedulerController
createSchedule:将调度的相关信息插入t_ds_schedules表中
online:此时才会将状态为online的流程加入调度
schedulerService.setScheduleState(loginUser, projectName, id, ReleaseState.ONLINE)
case ONLINE:setSchedule(project.getId(), id);
QuartzExecutors.getInstance().addJob(ProcessScheduleJob.class, jobName, jobGroupName, startDate, endDate,schedule.getCrontab(), dataMap)
*org.apache.dolphinscheduler.server.quartz.QuartzExecutors,是内部对Quartz进行的一个封装,仅仅提供增加、删除作业的基础功能。其作业的状态等信息保存在数据库中以QRTZ_开头的表。
为了将实际作业的定义与Quartz隔离,抽象了一个ProcessScheduleJob类,用它来创建JobDetail。该类仅仅是根据流程定义的定时等信息创建了一个CommandType.SCHEDULER类型的Command对象,然后插入了数据库,并没有的执行任务的具体逻辑。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
1.2、MasterSchedulerThread
对应架构图中的CommandScanner
这是一个扫描线程,定时扫描数据库中的 t_ds_command 表,根据不同的命令类型进行不同的业务操作。扫描的SQL如下:
- 1
- 2
select command.* from t_ds_command command
join t_ds_process_definition definition on command.process_definition_id = definition.id
where definition.release_state = 1 AND definition.flag = 1
order by command.update_time asc
limit 1
- 1
- 2
- 3
- 4
- 5
定时的默认是1秒,由Constants.SLEEP_TIME_MILLIS设置。Command的创建与执行是异步的。
MasterSchedulerThread类查询到一个Comamand后将其转化为一个ProcessInstance,交由MasterExecThread进行执行。
MasterSchedulerThread功能比较简单,就是负责衔接Quartz创建的Command,一个桥梁的作用:
即如果状态设置为online后,会将scheduler表中的记录添加到quartz中,quartz根据scheduler中设置的时间触发,触发后会向t_ds_command中添加一条记录,每一秒MasterSchedulerThread会扫描t_ds_command中的记录封装成ProcessInstance,交由MasterExecThread进行执行
- 1
- 2
- 3
- 4
1.3、MasterExecThread
负责执行ProcessInstance,功能主要是DAG任务切分、任务提交监控等其他逻辑处理。
DAG切割:首先找入度为0的任务(也就是没有任务依赖),放到准备提交队列;任务执行成功后,扫描后续的任务,如果该任务的所有依赖都成功,则执行该任务;循环处理。
MasterExecThread随着DAG中所有任务的执行结束而结束。在MasterExecThread中,也没有执行具体的任务逻辑,只是创建了一个MasterTaskExecThread负责任务的“执行”。
- 1
- 2
- 3
关键代码如下:
// execute flow
private void executeProcess() throws Exception {
prepareProcess();//init task queue and generate process dag
runProcess();//submit and watch the tasks, until the work flow stop,将dag中的task添加到不同的taskLis中
//先检查资源是否充足,submitStandByTask。submitTaskExec(task);将任务给MasterTaskExecThread执行
endProcess();//updateProcessInstance and createRecoveryWaitingThreadCommand(对于子流程)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
1.4、MasterTaskExecThread
其功能主要就是负责任务的持久化,简单来说就是把TaskInstacne信息保存到数据库中,同时如果一个任务满足执行条件,也会把任务ID提交到TaskQueue中的。
这个线程会每隔1秒(Constants.SLEEP_TIME_MILLIS设置)查询作业的状态,直到作业执行完毕(不管是成功还是失败
public TaskInstance submitTask(TaskInstance taskInstance, ProcessInstance processInstance){
logger.info("start submit task : {}, instance id:{}, state: {}, ",
taskInstance.getName(), processInstance.getId(), processInstance.getState() );
processInstance = this.findProcessInstanceDetailById(processInstance.getId());
//submit to mysql
TaskInstance task= submitTaskInstanceToMysql(taskInstance, processInstance);
if(task.isSubProcess() && !task.getState().typeIsFinished()){
ProcessInstanceMap processInstanceMap = setProcessInstanceMap(processInstance, task);
TaskNode taskNode = JSONUtils.parseObject(task.getTaskJson(), TaskNode.class);
Map<String, String> subProcessParam = JSONUtils.toMap(taskNode.getParams());
Integer defineId = Integer.parseInt(subProcessParam.get(Constants.CMDPARAM_SUB_PROCESS_DEFINE_ID));
createSubWorkProcessCommand(processInstance, processInstanceMap, defineId, task);
}else if(!task.getState().typeIsFinished()){
//submit to task queue
task.setProcessInstancePriority(processInstance.getProcessInstancePriority());
submitTaskToQueue(task);
}
logger.info("submit task :{} state:{} complete, instance id:{} state: {} ",
taskInstance.getName(), task.getState(), processInstance.getId(), processInstance.getState());
return task;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
1.5、TaskQueue
架构图中Master/Worker通信的重要渠道,它把待执行的队列放到了TaskQueue,由Worker获取到之后,执行具体的业务逻辑。根据技术架构介绍,这个TaskQueue是由Zookeeper实现。由此也可以看出,Master、Worker是没有直接的物理交互的。
关键代码如下:
protected ITaskQueue taskQueue = TaskQueueFactory.getTaskQueueInstance();
public static ITaskQueue getTaskQueueInstance() {
String queueImplValue = CommonUtils.getQueueImplValue();
if (StringUtils.isNotBlank(queueImplValue)) {
logger.info("task queue impl use zookeeper ");
return TaskQueueZkImpl.getInstance();
}else{
logger.error("property dolphinscheduler.queue.impl can't be blank, system will exit ");
System.exit(-1);
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
1.6、FetchTaskThread
org.apache.dolphinscheduler.server.worker.runner.FetchTaskThread循环从TaskQueue中获取任务,并根据不同任务类型调用TaskScheduleThread对应执行器。每次循环依旧休眠1秒。
List<String> tasksQueueList = taskQueue.getAllTasks(Constants.DOLPHINSCHEDULER_TASKS_QUEUE);
// submit task
workerExecService.submit(new TaskScheduleThread(taskInstance, processDao));
- 1
- 2
- 3
- 4
1.7、TaskScheduleThread
org.apache.dolphinscheduler.server.worker.runner.TaskScheduleThread负责任务的具体执行。构造获取任务相关的文件、参数等信息,创建Process类,执行对应的命令行,然后等待其执行完毕,获取标准输出、标准错误输出、返回码等信息
// update task state is running according to task type
updateTaskState(taskInstance.getTaskType());
//根据不同的task类型,产生不同的task实例类
task = TaskManager.newTask(taskInstance.getTaskType(),taskProps,taskLogger);
// task init
task.init();
// task handle
task.handle();
// task result process
task.after();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
1.8、LoggerServer
org.apache.dolphinscheduler.server.rpc.LoggerServer跟Worker、Master属于同一级别,都是需要单独启动的进程。这就是一个RPC服务器,提供日志分片查看、刷新和下载等功能。
2、项目架构
2.1、模块:
- dolphinscheduler-ui 前端页面模块
- dolphinscheduler-server 核心模块。包括master/worker等功能
- dolphinscheduler-common 公共模块。公共方法或类
- dolphinscheduler-api Restful接口。前后端交互层,与master/worker交互等功能
- dolphinscheduler-dao 数据操作层。实体定义、数据存储
- dolphinscheduler-alert 预警模块。与预警相关的方法、功能
- dolphinscheduler-rpc 日志查看。提供日志实时查看rpc功能
- dolphinscheduler-dist 与编译、分发相关的模块。没有具体逻辑功能
2.2、源码分析
从与UI交互的API模块开始着手,重点分析核心功能。(具体API文档见官网)
详见:https://www.cnblogs.com/gabry/p/12162272.html