DolphinScheduler1.2源码开发环境搭建及架构分析

一、本地开发环境的搭建

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、源码下载

  1. git branch -a #查看分支
  2. git checkout dev-db #切换到dev-db分支
  3. git pull #同步分支
    #由于项目前端的日志模块,使用了gRPC调用后端,所以需要先编译项目。
  4. 编译项目: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 数据库 ]


    mysql
    mysql-connector-java
    ${mysql.connector.version}
    

2.2、修改配置

1、dolphinscheduler-common的hadoop.properties主要修改的配置项有:

	fs.defaultFS=hdfs://xxxx
	yarn.resourcemanager.ha.rm.ids=192.168.0.244,192.168.0.245

2、dolphinscheduler-common的common.properties主要修改的配置项有:

	res.upload.startup.type=HDFS

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

4、dolphinscheduler-common的zookeeper.properties:配置连接及路径
5、dolphinscheduler-dao的application-dao.properties修改spring.datasource的相关配置

2.3、数据库初始化

  1. 创建ds数据库CREATE DATABASE dolphinscheduler
  2. 创建表和初始化数据: 修改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");

修改server模块resources目录下master_logback.xml文件,增加以下代码:

<root level="INFO">
	 <appender-ref ref="MASTERLOGFILE"/>
	 <!-- 增加日志到控制台-->
	 <appender-ref ref="STDOUT"/>
</root>

然后执行MasterServer即可。

2.4.2、启动WorkerServer

org.apache.dolphinscheduler.server.worker.WorkerServer类main函数增加如下代码:

System.setProperty("spring.profiles.active","worker");

修改server模块resources目录下worker_logback.xml文件,增加以下代码:

<root level="INFO">
	 <appender-ref ref="TASKLOGFILE"/>
	 <appender-ref ref="WORKERLOGFILE"/>
	 <!-- 增加日志到控制台-->
	 <appender-ref ref="STDOUT"/>
</root>

然后执行WorkerServer即可。

2.4.3、启动CombinedApplicationServer的mian函数下增加

System.setProperty("spring.profiles.active","combined");

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
DolphinScheduler1.2源码开发环境搭建及架构分析_第1张图片

二、源码结构分析

1、DolphinScheduler的架构图

DolphinScheduler1.2源码开发环境搭建及架构分析_第2张图片

架构说明:

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、MasterSchedulerThread

对应架构图中的CommandScanner
这是一个扫描线程,定时扫描数据库中的 t_ds_command 表,根据不同的命令类型进行不同的业务操作。扫描的SQL如下:
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秒,由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.3、MasterExecThread

负责执行ProcessInstance,功能主要是DAG任务切分、任务提交监控等其他逻辑处理。
DAG切割:首先找入度为0的任务(也就是没有任务依赖),放到准备提交队列;任务执行成功后,扫描后续的任务,如果该任务的所有依赖都成功,则执行该任务;循环处理。
MasterExecThread随着DAG中所有任务的执行结束而结束。在MasterExecThread中,也没有执行具体的任务逻辑,只是创建了一个MasterTaskExecThread负责任务的“执行”。

关键代码如下:

// 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.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.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.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.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.8、LoggerServer

org.apache.dolphinscheduler.server.rpc.LoggerServer跟Worker、Master属于同一级别,都是需要单独启动的进程。这就是一个RPC服务器,提供日志分片查看、刷新和下载等功能。

2、项目架构

2.1、模块:

  1. dolphinscheduler-ui 前端页面模块
  2. dolphinscheduler-server 核心模块。包括master/worker等功能
  3. dolphinscheduler-common 公共模块。公共方法或类
  4. dolphinscheduler-api Restful接口。前后端交互层,与master/worker交互等功能
  5. dolphinscheduler-dao 数据操作层。实体定义、数据存储
  6. dolphinscheduler-alert 预警模块。与预警相关的方法、功能
  7. dolphinscheduler-rpc 日志查看。提供日志实时查看rpc功能
  8. dolphinscheduler-dist 与编译、分发相关的模块。没有具体逻辑功能
    2.2、源码分析
    从与UI交互的API模块开始着手,重点分析核心功能。(具体API文档见官网)

详见:https://www.cnblogs.com/gabry/p/12162272.html

你可能感兴趣的:(大数据)