DolphinScheduler插件开发:自定义任务插件
概述
Apache DolphinScheduler 是一个分布式易扩展的可视化DAG(Directed Acyclic Graph,有向无环图)工作流任务调度系统。其强大的插件化架构允许开发者轻松扩展自定义任务类型,满足各种业务场景需求。本文将深入探讨如何开发自定义任务插件,从架构设计到具体实现,提供完整的开发指南。
插件架构概览
DolphinScheduler 采用模块化的插件架构,核心组件包括:
核心接口详解
1. TaskChannel 接口
TaskChannel 是插件入口点,负责创建任务实例和解析参数:
public interface TaskChannel {
AbstractTask createTask(TaskExecutionContext taskRequest);
AbstractParameters parseParameters(String taskParams);
}
2. AbstractTask 抽象类
所有任务插件必须继承 AbstractTask,实现核心生命周期方法:
public abstract class AbstractTask {
public abstract void init();
public abstract void handle(TaskCallBack taskCallBack) throws TaskException;
public abstract void cancel() throws TaskException;
public abstract AbstractParameters getParameters();
}
3. AbstractParameters 抽象类
参数类封装任务配置信息:
public abstract class AbstractParameters {
public abstract boolean checkParameters();
public abstract List<ResourceInfo> getResourceFilesList();
}
开发自定义任务插件:实战示例
步骤1:创建项目结构
my-custom-task-plugin/
├── src/main/java/org/apache/dolphinscheduler/plugin/task/custom/
│ ├── CustomTaskChannel.java
│ ├── CustomTask.java
│ ├── CustomParameters.java
│ └── CustomTaskExecutor.java
├── pom.xml
└── META-INF/services/org.apache.dolphinscheduler.spi.plugin.TaskPlugin
步骤2:实现参数类
@Getter
@Setter
public class CustomParameters extends AbstractParameters {
private String scriptContent;
private Integer timeout;
private List<ResourceInfo> resourceList;
private Map<String, String> environmentVariables;
@Override
public boolean checkParameters() {
return scriptContent != null && !scriptContent.trim().isEmpty();
}
@Override
public List<ResourceInfo> getResourceFilesList() {
return resourceList;
}
}
步骤3:实现任务通道
public class CustomTaskChannel implements TaskChannel {
@Override
public CustomTask createTask(TaskExecutionContext taskRequest) {
return new CustomTask(taskRequest);
}
@Override
public CustomParameters parseParameters(String taskParams) {
return JSONUtils.parseObject(taskParams, CustomParameters.class);
}
}
步骤4:实现任务逻辑
@Slf4j
public class CustomTask extends AbstractTask {
private CustomParameters parameters;
private final TaskExecutionContext context;
private final CustomTaskExecutor executor;
public CustomTask(TaskExecutionContext context) {
super(context);
this.context = context;
this.executor = new CustomTaskExecutor();
}
@Override
public void init() {
parameters = JSONUtils.parseObject(context.getTaskParams(), CustomParameters.class);
if (parameters == null || !parameters.checkParameters()) {
throw new TaskException("Custom task parameters validation failed");
}
log.info("Custom task initialized with parameters: {}", parameters);
}
@Override
public void handle(TaskCallBack callback) throws TaskException {
try {
// 执行自定义逻辑
ExecutionResult result = executor.execute(
parameters.getScriptContent(),
parameters.getTimeout(),
parameters.getEnvironmentVariables()
);
setExitStatusCode(result.getExitCode());
setProcessId(result.getProcessId());
// 处理输出参数
processOutputParameters(result.getOutput());
} catch (Exception e) {
log.error("Custom task execution failed", e);
setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);
throw new TaskException("Custom task execution error", e);
}
}
@Override
public void cancel() throws TaskException {
executor.cancel();
}
@Override
public AbstractParameters getParameters() {
return parameters;
}
private void processOutputParameters(String output) {
// 解析输出并设置参数
}
}
步骤5:创建SPI配置文件
在 META-INF/services/org.apache.dolphinscheduler.spi.plugin.TaskPlugin 中注册插件:
org.apache.dolphinscheduler.plugin.task.custom.CustomTaskChannel
步骤6:配置Maven依赖
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-task-api</artifactId>
<version>${dolphinscheduler.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-common</artifactId>
<version>${dolphinscheduler.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
插件部署与集成
部署方式
| 部署方式 | 说明 | 适用场景 |
|---|---|---|
| 独立JAR包 | 将插件打包成JAR,放入插件目录 | 开发测试环境 |
| Maven依赖 | 作为依赖集成到主项目 | 生产环境 |
| 动态加载 | 通过SPI机制动态加载 | 热部署场景 |
配置示例
在 plugin.properties 中配置插件信息:
# 自定义任务插件配置
custom.task.plugin.name=CustomTask
custom.task.plugin.description=自定义任务执行插件
custom.task.plugin.version=1.0.0
custom.task.plugin.author=YourName
最佳实践与注意事项
1. 参数验证
@Override
public boolean checkParameters() {
if (StringUtils.isBlank(scriptContent)) {
return false;
}
if (timeout != null && timeout <= 0) {
return false;
}
return true;
}
2. 异常处理
try {
// 业务逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Task interrupted", e);
setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);
throw new TaskException("Task interrupted", e);
} catch (TimeoutException e) {
log.warn("Task timeout", e);
setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);
throw new TaskException("Task timeout", e);
} catch (Exception e) {
log.error("Unexpected error", e);
setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);
throw new TaskException("Unexpected error", e);
}
3. 资源管理
@Override
public List<ResourceInfo> getResourceFilesList() {
List<ResourceInfo> resources = new ArrayList<>();
if (resourceList != null) {
resources.addAll(resourceList);
}
// 添加默认资源
resources.add(new ResourceInfo("default-config.properties", "config"));
return resources;
}
调试与测试
单元测试示例
@Test
public void testCustomTaskExecution() {
CustomParameters parameters = new CustomParameters();
parameters.setScriptContent("echo 'Hello World'");
parameters.setTimeout(30);
TaskExecutionContext context = new TaskExecutionContext();
context.setTaskParams(JSONUtils.toJsonString(parameters));
CustomTask task = new CustomTask(context);
task.init();
// 执行测试
task.handle(new SimpleTaskCallBack());
assertEquals(TaskConstants.EXIT_CODE_SUCCESS, task.getExitStatusCode());
}
集成测试流程
性能优化建议
1. 连接池管理
public class ConnectionPoolManager {
private static final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();
public static Connection getConnection(String dataSourceName) throws SQLException {
DataSource dataSource = dataSourceMap.computeIfAbsent(dataSourceName,
key -> createDataSource(key));
return dataSource.getConnection();
}
}
2. 异步处理
@Override
public void handle(TaskCallBack callback) throws TaskException {
CompletableFuture.runAsync(() -> {
try {
// 异步执行耗时操作
executeAsyncLogic();
callback.onSuccess();
} catch (Exception e) {
callback.onFailure(e);
}
});
}
常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 插件加载失败 | SPI配置错误 | 检查META-INF/services文件 |
| 参数解析异常 | JSON格式错误 | 验证参数格式 |
| 任务执行超时 | 资源不足或逻辑复杂 | 优化代码或增加超时时间 |
| 内存泄漏 | 资源未正确释放 | 使用try-with-resources |
总结
DolphinScheduler 的插件架构提供了强大的扩展能力,通过实现 TaskChannel、AbstractTask 和 AbstractParameters 三个核心接口,开发者可以轻松创建自定义任务插件。本文详细介绍了插件开发的完整流程,包括架构设计、代码实现、部署集成和性能优化等方面。
关键要点:
- 遵循插件接口规范,确保兼容性
- 完善的参数验证和异常处理机制
- 合理的资源管理和性能优化
- 全面的测试覆盖和问题排查方案
通过自定义任务插件,可以扩展 DolphinScheduler 的功能边界,满足各种复杂的业务场景需求,为企业的数据调度和流程自动化提供强有力的技术支持。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



