2021SC@SDUSC
10-11
DolphinScheduler(3)
10-11—2021SC@SDUSC-----DolphinScheduler(3)-----ProcessDefinitionService
org.apache.dolphinscheduler.api.service.ProcessDefinitionService承担流程定义具体的CURD逻辑,调用各种mapper、dao。
如下
public Map<String, Object> createProcessDefinition(User loginUser, String projectName, String name,
String processDefinitionJson, String desc, String locations, String connects) throws JsonProcessingException {
Map<String, Object> result = new HashMap<>(5);
Project project = projectMapper.queryByName(projectName);
// check project auth
Map<String, Object> checkResult = projectService.checkProjectAndAuth(loginUser, project, projectName);
Status resultStatus = (Status) checkResult.get(Constants.STATUS);
if (resultStatus != Status.SUCCESS) {
return checkResult;
}
ProcessDefinition processDefine = new ProcessDefinition();
Date now = new Date();
ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class);
Map<String, Object> checkProcessJson = checkProcessNodeList(processData, processDefinitionJson);
if (checkProcessJson.get(Constants.STATUS) != Status.SUCCESS) {
return checkProcessJson;
}
processDefine.setName(name);
processDefine.setReleaseState(ReleaseState.OFFLINE);
processDefine.setProjectId(project.getId());
processDefine.setUserId(loginUser.getId());
processDefine.setProcessDefinitionJson(processDefinitionJson);
processDefine.setDescription(desc);
processDefine.setLocations(locations);
processDefine.setConnects(connects);
processDefine.setTimeout(processData.getTimeout());
processDefine.setTenantId(processData.getTenantId());
//custom global params
List<Property> globalParamsList = processData.getGlobalParams();
if (globalParamsList != null && globalParamsList.size() > 0) {
Set<Property> globalParamsSet = new HashSet<>(globalParamsList);
globalParamsList = new ArrayList<>(globalParamsSet);
processDefine.setGlobalParamList(globalParamsList);
}
processDefine.setCreateTime(now);
processDefine.setUpdateTime(now);
processDefine.setFlag(Flag.YES);
processDefineMapper.insert(processDefine);
putMsg(result, Status.SUCCESS);
result.put("processDefinitionId",processDefine.getId());
return result;
}
具体分析:
研读上面代码我们知道createProcessDefinition大概有以下功能:
校验当前用户是否拥有所属项目的权限
校验流程定义JSON是否合法。例如是否有环
构造ProcessDefinition对象插入数据库
设置HTTP返回结果
因为这些都不是核心逻辑,都不再深入展开。
dolphinscheduler-api其他的功能都不在分析,
因为到此流程定义信息已经写入到了数据库,跟API模块已经没有关系了。
但要知道ProcessDefinition对象插入到了哪张表,
这样才知道如何查询、更新这个表的。这个表就是前后台逻辑交互的关键。
从ProcessDefinition可以看出,数据最终插入t_ds_process_definition表
@Data
@TableName("t_ds_process_definition")
public class ProcessDefinition
其实也可以不用关注具体插入到了哪张表,好像只需要关系哪个地方用ProcessDefinitionMapper查询了数据就行了。
但根据之前的概念定义,我们知道每个流程定义是需要靠“定时”周期性触发的,这样的话我们可以猜测,系统并不会直接用ProcessDefinitionMapper查询流程定义,而是会根据定时关联的ProcessDefinition来调起DAG。这一点在MasterSchedulerThread的分析中已经可以看出来了。
SchedulerController、SchedulerService
考虑到Controler逻辑比较清楚,因而此处将controller和service合并分析。
同样SchedulerController几乎没有逻辑,全都交给了SchedulerService层。这里只分析SchedulerService.insertSchedule,简单浏览代码后,可以发现它跟createProcessDefinition逻辑差不多:
1.检验当前用户是否拥有所属项目的权限
2.检验流程定义JSON是否合法。例如是否有环
3.构造Schedule对象插入数据库
- 设置HTTP返回结果
当然除了上面4点还查询、更新了ProcessDefinition,主要是将Schedule和ProcessDefinition进行关联。
MasterSchedulerThread
Package org.apache.dolphinscheduler.server.master.runner;
import ···
/* master schelduler thread */
public class MasterSchechdulerThread implements Runnable {
/**logger of MasterSchechdulerThread*/
private static final Logger logger = LoggerFactory.getLogger(MasterSchedulerThread.class);
/** master exec service */
private final ExecutorService masterExecService;
/**dolphinscheduler databases interface */
private final ProcessDao processDao;
/**zookeeper master client */
private final ZKMasterClient zkMasterClient;
/** master exec thead num*/
private int masterExecThreadNum;
/**Configuration of MasterSchedulerThead */
private final Configuration conf;
/** constructor of MasterSchedulerThead...*/
public MasterSchedulerThead(ZKMasterClient zkClient,processDao processDao ,Configuration conf,int masterExecThreadNum){...}
/** run of MasterSchedulerThread*/
@Override
public void run(){ ligang,2019/3/29 13:49·Initial module eschduler-server commit
while(Stopper.isRunner()){...}
}
}
以上是MasterSchedulerThread类的概览图。
MasterSchedulerThread实现Runnable接口,很明显主要的逻辑应该在run方法内,而且根据经验以及前面的分析可以知道,这个方法内是一个“死”循环,且为了避免CPU飙升,会休眠一小段时间。
下面我们逐步展开、分析MasterSchedulerThread类
/**run of MasterSchedulerThread*/
@Override
public void run(){
while (Stopper.isRunning()){
//process instance
ProcessInstance processInstance = null;
InterProcessMutex mutex = null;
try{
if(OSUtills.checResource(conf,isMaster.true)){
//create distributed lock
String znodeLock =zkMaskterClient.getMasterLockerPath();
mutex =new InterProcessMutex(zkMasterClient.getActiveCount);
mutex.acquire();
ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor)masterExecService;
int activeCount = poolExecutor.getActiveCount();
//make sure to scan and delete commend table in one transaction
Command commend =processDao.findOneCommend();
if(commend !=null){...}
}
}
//acessing the commend table every SLEEP_TIME_MILLTS milliseconds
Thread.sleep(Constants.SLEEP_TIME_MILLIS);
}Catch(Exception e){
Logger.error(''master scheduler thread exception : '' +e.getMessage(),e);
}finally{
}AbstractZKClient.releaseMutex(mutex);
}
}
从上图简单分析,总结一下run的逻辑:
- 调用OSUtils.checkResource,检查当前资源(内存、CPU)。
2.资源超出阈值,则休眠1秒进入下一次循环。 - 检查zookeeper是否连接成功
4.获取一个InterProcessMutex锁(分布式的公平可重入互斥锁)。也就是只有一个master可以获取到这个锁 - 查询一个Command,不为null时进行后续逻辑。
6.休眠1秒,进入下一次循环 - 进入下一次循环之前,释放InterProcessMutex锁