Oozie分布式工作流——从理论和实践分析使用节点间的参数传递
Oozie支持Java Action,因此可以自定义很多的功能。本篇就从理论和实践两方面介绍下Java Action的妙用,另外还涉及到oozie中action之间的参数传递。
本文大致分为以下几个部分:
- Java Action教程文档
- 自定义Java Action实践
- 从源码的角度讲解Java Action与Shell Action的参数传递。
如果你即将或者想要使用oozie,那么本篇的文章将会为你提供很多参考的价值。
Java Action文档
java action会自动执行提供的java class的public static void main方法, 并且会在hadoop集群启动一个单独的map-reduce的map任务来执行的。因此,如果你自定义了一个java程序,它会提交到集群的某一个节点执行,不会每个节点都执行一遍。
workflow任务会等待java程序执行完继续执行下一个action。当java类正确执行退出后,将会进入ok控制流;当发生异常时,将会进入error控制流。Java程序绝对不能使用System.exit(int n)将会导致action进入error控制流。
在action的配置中,也支持EL表达式。并且使用<capture-output>也可以把数据输出出来,然后后面的action就可以基于EL表达式使用了。
语法规则
<workflow-app name="[WF-DEF-NAME]" xmlns="uri:oozie:workflow:0.1">
...
<action name="[NODE-NAME]">
<java>
<job-tracker>[JOB-TRACKER]</job-tracker>
<name-node>[NAME-NODE]</name-node>
<prepare>
<delete path="[PATH]"/>
...
<mkdir path="[PATH]"/>
...
</prepare>
<job-xml>[JOB-XML]</job-xml>
<configuration>
<property>
<name>[PROPERTY-NAME]</name>
<value>[PROPERTY-VALUE]</value>
</property>
...
</configuration>
<main-class>[MAIN-CLASS]</main-class>
<java-opts>[JAVA-STARTUP-OPTS]</java-opts>
<arg>ARGUMENT</arg>
...
<file>[FILE-PATH]</file>
...
<archive>[FILE-PATH]</archive>
...
<capture-output />
</java>
<ok to="[NODE-NAME]"/>
<error to="[NODE-NAME]"/>
</action>
...
</workflow-app>
prepare元素,支持创建或者删除指定的文件内容。在delete时,支持通配的方式指定特定的路径。java-opts以及java-opt参数提供了执行java应用时分配的JVM。
举个例子:
<workflow-app name="sample-wf" xmlns="uri:oozie:workflow:0.1">
...
<action name="myfirstjavajob">
<java>
<job-tracker>foo:8021</job-tracker>
<name-node>bar:8020</name-node>
<prepare>
<delete path="${jobOutput}"/>
</prepare>
<configuration>
<property>
<name>mapred.queue.name</name>
<value>default</value>
</property>
</configuration>
<main-class>org.apache.oozie.MyFirstMainClass</main-class>
<java-opts>-Dblah</java-opts>
<arg>argument1</arg>
<arg>argument2</arg>
</java>
<ok to="myotherjob"/>
<error to="errorcleanup"/>
</action>
...
</workflow-app>
覆盖Main方法
oozie中的很多action都支持这个功能,在configure中指定classpath下的一个类方法,它会覆盖当前action的main方法。这在不想重新编译jar包,而想替换程序时,非常有用。
自定义Java action程序以及部署
Java程序可以任意定义,比如写一个最简单的hellword,然后打包成lib。
然后需要定义oozie脚本:
<action name="java-7cbb">
<java>
<job-tracker>${jobTracker}</job-tracker>
<name-node>${nameNode}</name-node>
<configuration>
<property>
<name>mapred.job.queue.name</name>
<value>default</value>
</property>
</configuration>
<main-class>a.b.c.Main</main-class>
<arg>arg1</arg>
<arg>arg2</arg>
<file>/oozie/lib/ojdbc7.jar#ojdbc7.jar</file>
<capture-output/>
</java>
<ok to="end"/>
<error to="Kill"/>
</action>
其中几个比较重要的属性,千万不能拉下:
- 1 需要指定Map-reduce的队列:
mapred.job.queue.name - 2 指定Main class
<main-class> - 3 如果依赖其他的jar,需要添加
<file> - 4 如果想要捕获输出,需要设置
<capture-output>
如果使用HUE图形化配置,就比较简单了:

点击右上角的齿轮,配置其他的属性信息:

基于源码分析参数传递
先从表象来说一下shell action如何传递参数:
你只需要定义一个普通的shell,在里面使用echo把属性输出出来即可,后面的action自动就可以基于EL表达式使用。
test='test123'
echo "test=$test"
这样后面的action就可以直接使用了:
${wf:actionData('action-name').test}或者${wf:actionData('action-name')['test']}
很简单是吧!
在Java里面就没这么容易了:
无论是 System.out.println() 还是 logger.info/error,都无法捕获到数据
从中抄了一段代码:
private static final String OOZIE_ACTION_OUTPUT_PROPERTIES = "oozie.action.output.properties";
...
String oozieProp = System.getProperty(OOZIE_ACTION_OUTPUT_PROPERTIES);
if (oozieProp != null) {
File propFile = new File(oozieProp);
Properties props = new Properties();
props.setProperty(propKey0, propVal0);
props.setProperty(propKey1, propVal1);
OutputStream os = new FileOutputStream(propFile);
props.store(os, "");
os.close();
} else
throw new RuntimeException(OOZIE_ACTION_OUTPUT_PROPERTIES + " System property not defined");
果然就好用了....
为了理解其中的缘由,我们来看看代码。首先在shell action中发现一句话:
<<< Invocation of Main class completed <<<
Oozie Launcher, capturing output data:
=======================
于是全局搜索,果然找到对应的代码,在org.apache.oozie.action.hadoop.LuancherMapper.java中,line275开始:
if (errorMessage == null) {
handleActionData();
if (actionData.get(ACTION_DATA_OUTPUT_PROPS) != null) {
System.out.println();
System.out.println("Oozie Launcher, capturing output data:");
System.out.println("=======================");
System.out.println(actionData.get(ACTION_DATA_OUTPUT_PROPS));
System.out.println();
System.out.println("=======================");
System.out.println();
}
。。。
}
这里的actionData其实就是个普通的MAP
private Map<String,String> actionData;
public LauncherMapper() {
actionData = new HashMap<String,String>();
}
Map里面保存了很多属性值,其中就包括我们想要捕获的输出内容:
static final String ACTION_PREFIX = "oozie.action.";
static final String ACTION_DATA_OUTPUT_PROPS = "output.properties";
...
String outputProp = System.getProperty(ACTION_PREFIX + ACTION_DATA_OUTPUT_PROPS);
if (outputProp != null) {
File actionOutputData = new File(outputProp);
if (actionOutputData.exists()) {
int maxOutputData = getJobConf().getInt(CONF_OOZIE_ACTION_MAX_OUTPUT_DATA, 2 * 1024);
actionData.put(ACTION_DATA_OUTPUT_PROPS,
getLocalFileContentStr(actionOutputData, "Output", maxOutputData));
}
}
....
public static String getLocalFileContentStr(File file, String type, int maxLen) throws LauncherException, IOException {
StringBuffer sb = new StringBuffer();
FileReader reader = new FileReader(file);
char[] buffer = new char[2048];
int read;
int count = 0;
while ((read = reader.read(buffer)) > -1) {
count += read;
if (maxLen > -1 && count > maxLen) {
throw new LauncherException(type + " data exceeds its limit ["+ maxLen + "]");
}
sb.append(buffer, 0, read);
}
reader.close();
return sb.toString();
}
可以看到其实就是从oozie.action.output.properties指定的目录里面去读内容,然后输出出来,后面的action就可以用了。这就是为什么上面抄的那段代码可以使用的原因。
那么问题是,shell为什么直接echo就行,java里面却要这么费劲?
别急,先来看看java action的启动逻辑:
public static void main(String[] args) throws Exception {
run(JavaMain.class, args);
}
@Override
protected void run(String[] args) throws Exception {
...
Class<?> klass = actionConf.getClass(JAVA_MAIN_CLASS, Object.class);
...
Method mainMethod = klass.getMethod("main", String[].class);
try {
mainMethod.invoke(null, (Object) args);
} catch(InvocationTargetException ex) {
// Get rid of the InvocationTargetException and wrap the Throwable
throw new JavaMainException(ex.getCause());
}
}
它什么也没做,就是启动了目标类的main方法而已。
再来看看shell:
private int execute(Configuration actionConf) throws Exception {
...
//判断是否要捕获输出
boolean captureOutput = actionConf.getBoolean(CONF_OOZIE_SHELL_CAPTURE_OUTPUT, false);
//执行命令
Process p = builder.start();
//处理进程
Thread[] thrArray = handleShellOutput(p, captureOutput);
...
return exitValue;
}
protected Thread[] handleShellOutput(Process p, boolean captureOutput)
throws IOException {
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// 捕获标准输出
OutputWriteThread thrStdout = new OutputWriteThread(input, true, captureOutput);
thrStdout.setDaemon(true);
thrStdout.start();
OutputWriteThread thrStderr = new OutputWriteThread(error, false, false);
thrStderr.setDaemon(true);
thrStderr.start();
return new Thread[]{ thrStdout, thrStderr };
}
class OutputWriteThread extends Thread {
...
@Override
public void run() {
String line;
BufferedWriter os = null;
//读取数据保存在目标文件中
try {
if (needCaptured) {
File file = new File(System.getProperty(LauncherMapper.ACTION_PREFIX + LauncherMapper.ACTION_DATA_OUTPUT_PROPS));
os = new BufferedWriter(new FileWriter(file));
}
while ((line = reader.readLine()) != null) {
if (isStdout) { // For stdout
// 1. Writing to LM STDOUT
System.out.println("Stdoutput " + line);
// 2. Writing for capture output
if (os != null) {
if (Shell.WINDOWS) {
line = line.replace("\\u", "\\\\u");
}
os.write(line);
os.newLine();
}
}
else {
System.err.println(line); // 1. Writing to LM STDERR
}
}
}
catch (IOException e) {
...
}finally {
...
}
}
}
这样就很清晰了,shell自动帮我们把输出的内容写入了oozie.action.output.properties文件中。而在java中则需要用户自己来定义写入的过程。
后续将会介绍一下oozie中比较高级的用法——EL表达式
Oozie分布式工作流——从理论和实践分析使用节点间的参数传递的更多相关文章
- CAN调度理论与实践分析
CAN调度理论与实践分析 分布式嵌入式系统是当前嵌入式系统的重要发展方向,因为它能提供更强的性能,节约系统的总体成本.但是由于各单个节点必须有通信网络相连才能协调地工作,网络就成了关键部分,没有网络提 ...
- Oozie分布式工作流——流控制
最近又开始捅咕上oozie了,所以回头还是翻译一下oozie的文档.文档里面最重要就属这一章了--工作流定义. 一提到工作流,首先想到的应该是工作流都支持哪些工作依赖关系,比如串式的执行,或者一对多, ...
- Oozie分布式工作流——Action节点
前篇讲述了下什么是流控制节点,本篇继续来说一下什么是 Action Nodes操作节点.Action节点有一些比较通用的特性: Action节点是远程的 所有oozie创建的计算和处理任务都是异步的, ...
- Oozie分布式工作流——EL表达式
oozie支持使用EL(expression language)表达式. 基本的EL常量 KB MB GB TB PB 基本EL函数 string firstNotNull(String value1 ...
- Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践
Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践 Java生鲜电商平台微服务现状 某个服务挂了,导致上游大量报警,如何快速定位哪个服务出问题? 某个核心挂了,导致大量报错,如何 ...
- 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法
若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...
- DDD(领域驱动设计)理论结合实践
DDD(领域驱动设计)理论结合实践 写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听 ...
- Java 理论与实践: 用弱引用堵住内存泄漏
弱引用使得表达对象生命周期关系变得容易了 虽然用 Java™ 语言编写的程序在理论上是不会出现“内存泄漏”的,但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集.本月,负责保障应用程序健 ...
- 监督学习——决策树理论与实践(下):回归决策树(CART)
介绍 决策树分为分类决策树和回归决策树: 上一篇介绍了分类决策树以及Python实现分类决策树: 监督学习——决策树理论与实践(上):分类决策树 决策树是一种依托决策而建立起来的一种 ...
随机推荐
- (转)eclipse 创建maven web项目
1.新建Maven项目 1.1 File -> New -> Other 1.2 选择Maven Project ,单击Next 1.3 保持默认即可,单击Next 1.4 选择Arche ...
- 性能测试二十一:环境部署之mysql
在正常工作中,mysql应该部署到 一台独立的服务器上,不与tomcat共用服务器,由于成本原因,现部署到一起 为避免出错引起麻烦,先备份: 一:环境清理:先卸载系统自带的mysql 停止mysql: ...
- 步步为营-21-xml的增删改查
1 增加(存在则添加,不存在则新建) //对xml的操作-- XmlDocument doc = new XmlDocument(); if (File.Exists("Person.xml ...
- 解决在使用pip list时出现DEPRECATION
在C:\Users\Administrator下建立pip文件夹,在pip下新建pip.ini:内容为: [list]format=columns
- java.lang.NumberFormatException: multiple points问题
一般这种问题主要是因为SimpleDateFormat在多线程环境下,是线程不安全的,所以如果你在多线程环境中共享了SimpleDateFormat的实例,比如你在类似日期类中定义了一个全局的Simp ...
- 删除input上传的文件路径
上传文件时,选择了文件后想清空文件路径,搜索了一下,用两种方法解决 <input type="file" id="fileupload" name=&qu ...
- BZOJ4977 八月月赛 Problem G 跳伞求生 set 贪心
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4977 - 八月月赛 Problem G 题意 小明组建了一支由n名玩家组成的战队,编号依次为1到n ...
- CodeBlocks切换中文
下载汉化包,百度搜索 “codeblock汉化包”即可, 比如:http://www.jb51.net/softs/545123.html 将其拷贝到,此路径: X:\Program Files (x ...
- POJ - 3080 Blue Jeans 【KMP+暴力】(最大公共字串)
<题目链接> 题目大意: 就是求k个长度为60的字符串的最长连续公共子串,2<=k<=10 限制条件: 1. 最长公共串长度小于3输出 no significant co ...
- js数据结构之二叉树的详细实现方法
数据结构中,二叉树的使用频率非常高,这得益于二叉树优秀的性能. 二叉树是非线性的数据结构,用以存储带有层级的数据,其用于查找的删除的性能非常高. 二叉树 数据结构的实现方法如下: function N ...