版本:oozie 5.1
官网文档地址:https://oozie.apache.org/docs/5.1.0/DG_CustomActionExecutor.html
实现步骤:
- 实现
org.apache.oozie.action.ActionExecutor
接口 - 修改oozie配置
- 上传jar包
- 重启服务
实现接口
实现 org.apache.oozie.action.ActionExecutor
接口
- 调用父类
super(type)
方法,其中传入的type
就是自定义类注册的类型名称 start()
,该方法会在调用节点的时候执行。在该方法中必须调用setStartData()
方法,并且值不能为null
。如果想要实现跳过功能,还可以调用setExecutionData()
方法直接将状态修改为成功。end()
,该方法在节点正常执行结束的时候执行。在该方法中必须调用setEndData()
方法,值同样不能为null
。check()
,该方法用于检测节点是否执行完成。并且,如果是一个异步节点(异步节点,任务不是实际在oozie上执行的:比如一个hive
节点,任务实际上提交到yarn
执行,那么check
就是主动去yarn
上查询状态),则必须实现该方法,并且在节点完成时,必须调用setExecutionData()
方法,如果节点仍然在执行,则必须调用setExternalStatus()
方法kill()
,用于主动结束任务的实现。必须调用setEndData()
方法isCompleted()
,判断当前节点是否为任务的终态
pom
依赖
<dependency>
<groupId>org.apache.oozie</groupId>
<artifactId>oozie-core</artifactId>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
示例代码:比如我们要实现一个节点去调起一个外部程序,并要等待该程序执行完成,这里没有具体逻辑,只是简单的伪代码
package net.zhboom.node;
import org.apache.oozie.action.ActionExecutor;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.client.WorkflowAction;
import java.util.HashSet;
import java.util.Set;
/**
* @author zhboom
* @date 2023/5/19
*/
public class CustomOozieAction extends ActionExecutor {
private static final String ACTION_TYPE = "custom-oozie";
private final CallExternalProgram callExternalProgram = new CallExternalProgram();
public CustomOozieAction() {
super(ACTION_TYPE);
}
@Override
public void start(Context context, WorkflowAction action) throws ActionExecutorException {
try {
// 可以拿到 XML 中定义的内容
Element eConf = XmlUtils.parseXml(action.getConf());
Namespace ns = eConf.getNamespace();
String executePath = eConf.getChild("executor-path", ns).getTextTrim();
String command = eConf.getChild("command", ns).getTextTrim();
callExternalProgram.start();
// 这里的数据根据后边会用到
context.setStartData("-", "-", "-");
} catch (JDOMException e) {
e.printStackTrace();
}
}
@Override
public void end(Context context, WorkflowAction action) throws ActionExecutorException {
// 这里是复制的子工作流节点的逻辑
String externalStatus = action.getExternalStatus();
WorkflowAction.Status status = externalStatus.equals("SUCCEEDED") ? WorkflowAction.Status.OK
: WorkflowAction.Status.ERROR;
context.setEndData(status, getActionSignal(status));
}
@Override
public void check(Context context, WorkflowAction action) throws ActionExecutorException {
Status status = callExternalProgram.checkStatus();
switch (status) {
case FAILED:
case SUCCESS:
context.setExecutionData("对应的状态", null);
break;
default:
context.setExternalStatus("对应的状态");
}
}
@Override
public void kill(Context context, WorkflowAction action) throws ActionExecutorException {
callExternalProgram.shutdown();
context.setEndData(WorkflowAction.Status.KILLED, getActionSignal(WorkflowAction.Status.KILLED));
}
private static Set<String> FINAL_STATUS = new HashSet<String>();
static {
FINAL_STATUS.add("SUCCEEDED");
FINAL_STATUS.add("KILLED");
FINAL_STATUS.add("FAILED");
}
@Override
public boolean isCompleted(String externalStatus) {
return FINAL_STATUS.contains(externalStatus);
}
static class CallExternalProgram {
public void start() {
// 调用外部程序
}
public Status checkStatus() {
return Status.SUCCESS;
}
public void shutdown() {
// 终止外部程序
}
}
public enum Status {
SUCCESS, FAILED
}
}
在 resource
目录下新建文件 customOozieAction.xsd
,这里的数据可以通过参数 action
拿到。
有关 .xsd
文件的定义可自定百度
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:customOozie="uri:custom:oozie-action:0.1"
elementFormDefault="qualified"
targetNamespace="uri:custom:oozie-action:0.1"
>
<xs:complexType name="CONFIGURATION">
<xs:sequence>
<xs:element name="property" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" minOccurs="1" maxOccurs="1" type="xs:string"/>
<xs:element name="value" minOccurs="1" maxOccurs="1" type="xs:string"/>
<xs:element name="description" minOccurs="0" maxOccurs="1" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FLAG"/>
<xs:complexType name="CUSTOM-OOZIE">
<xs:sequence>
<xs:element name="app-path" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="propagate-configuration" type="customOozie:FLAG" minOccurs="0" maxOccurs="1"/>
<!-- 外部程序地址 -->
<xs:element name="executor-path" type="xs:string" />
<!-- 外部程序执行指令 -->
<xs:element name="command" type="xs:string" />
<xs:element name="configuration" type="customOozie:CONFIGURATION" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:element name="custom-oozie" type="customOozie:CUSTOM-OOZIE"/>
</xs:schema>
修改 oozie 配置
在 oozie-site.xml
文件中修改配置 ,多个用英文逗号分割
oozie.service.ActionService.executor.ext.classes
:net.zhboom.node.CustomOozieAction
oozie.service.SchemaService.wf.ext.schemas
:customOozieAction.xsd
<property>
<name>oozie.service.ActionService.executor.ext.classes</name>
<value>net.zhboom.node.CustomOozieAction</value>
</property>
<property>
<name>oozie.service.SchemaService.wf.ext.schemas</name>
<value>customOozieAction.xsd</value>
</property>
上传 jar 包
将代码打包,并上传到 oozie server 对应的 libext/
目录下(注意修改文件的权限)
如果有多台服务,则每台都要放
重启
然后重启 oozie 服务就可以使用自定义节点了