oozie教程

来源:互联网 发布:长期股权投资商誉算法 编辑:程序博客网 时间:2024/06/06 03:11

文章转载自 http://blog.csdn.net/nsrainbow/article/details/43746111 

Oozie的3个概念

Oozie有3个主要概念
  • workflow  工作流
  • coordinator 多个workflow可以组成一个coordinator,可以把前几个workflow的输出作为后一个workflow的输入,也可以定义workflow的触发条件,来做定时触发
  • bundle 是对一堆coordinator的抽象
以下这幅图解释了Oozie组件之间的关系

hPDL

oozie采用一种叫 hPDL的xml规范来定义工作流。

这是一个wordcount版本的hPDL的xml例子
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <workflow-app name='wordcount-wf' xmlns="uri:oozie:workflow:0.1">  
  2.     <start to='wordcount'/>  
  3.     <action name='wordcount'>  
  4.         <map-reduce>  
  5.             <job-tracker>${jobTracker}</job-tracker>  
  6.             <name-node>${nameNode}</name-node>  
  7.             <configuration>  
  8.                 <property>  
  9.                     <name>mapred.mapper.class</name>  
  10.                     <value>org.myorg.WordCount.Map</value>  
  11.                 </property>  
  12.                 <property>  
  13.                     <name>mapred.reducer.class</name>  
  14.                     <value>org.myorg.WordCount.Reduce</value>  
  15.                 </property>  
  16.                 <property>  
  17.                     <name>mapred.input.dir</name>  
  18.                     <value>${inputDir}</value>  
  19.                 </property>  
  20.                 <property>  
  21.                     <name>mapred.output.dir</name>  
  22.                     <value>${outputDir}</value>  
  23.                 </property>  
  24.             </configuration>  
  25.         </map-reduce>  
  26.         <ok to='end'/>  
  27.         <error to='end'/>  
  28.     </action>  
  29.     <kill name='kill'>  
  30.         <message>Something went wrong: ${wf:errorCode('wordcount')}</message>  
  31.     </kill/>  
  32.     <end name='end'/>  
  33. </workflow-app>  

这个例子可以用以下这幅图表示



一个oozie job的组成

一个oozie 的 job 一般由以下文件组成
  • job.properties 记录了job的属性
  • workflow.xml 使用hPDL 定义任务的流程和分支
  • class 文件,用来执行具体的任务
任务启动的命令一般长这样子
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. $ oozie job -oozie http://localhost:11000/oozie -config examples/apps/map-reduce/job.properties -run  

可以看到 任务开始是通过调用 oozie  job 命令并传入oozie服务器地址和 job.properties 的路径开始。job.properties 是一个任务的执行入口

做个MapReduce例子

这里使用官方提供的例子。

Step1

在 host1 上下载oozie包
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. wget http://apache.fayea.com/oozie/4.1.0/oozie-4.1.0.tar.gz  

解压开,里面有一个 examples文件夹,我们将这个文件夹拷贝到别的地方,并改名为 oozie-examples 
进入这个文件夹,然后修改pom.xml,在plugins里面增加一个plugin
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <plugin>  
  2.     <groupId>org.apache.maven.plugins</groupId>  
  3.     <artifactId>maven-surefire-plugin</artifactId>  
  4.     <version>2.5</version>  
  5.         <configuration>  
  6.             <skipTests>false</skipTests>  
  7.             <testFailureIgnore>true</testFailureIgnore>  
  8.             <forkMode>once</forkMode>  
  9.         </configuration>  
  10. </plugin>  
然后运行 mvn package 可以看到 target 文件夹下有 oozie-examples-4.1.0.jar

Step2

编辑 oozie-examples/src/main/apps/map-reduce/job.properties
修改 namenode为hdfs 的namenode地址,因为我们是搭成ha模式,所以写成 hdfs://mycluster 。修改 jobTracker为 resoucemanager 所在的地址,这边为 host1:8032 
改完后的 job.properties 长这样
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. nameNode=hdfs://mycluster  
  2. jobTracker=host1:8032  
  3. queueName=default  
  4. examplesRoot=examples  
  5.   
  6. oozie.wf.application.path=${nameNode}/user/${user.name}/${examplesRoot}/apps/map-reduce  
  7. outputDir=map-reduce  

这里的 user.name 就是你运行oozie的linux 用户名,我用的是root,所以最后的路径会变成 hdfs://mycluster/user/root/examples/apps/map-reduce

Step3

根据上面配置的路径,我们在hdfs上先建立出 /user/root/examples/apps/map-reduce/ 目录
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -mkdir -p /user/root/examples/apps/map-reduce  

然后把 src/main/apps/map-reduce/workflow.xml 传到 /user/root/examples/apps/map-reduce 下面
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -put oozie-examples/src/main/apps/map-reduce/workflow.xml /user/root/examples/apps/map-reduce/  
在 /user/root/examples/apps/map-reduce/ 里面建立 lib 文件夹,并把 打包好的 oozie-examples-4.1.0.jar 上传到这个目录下

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -mkdir /user/root/examples/apps/map-reduce/lib  
  2. hdfs dfs -put oozie-examples/target/oozie-examples-4.1.0.jar /user/root/examples/apps/map-reduce/lib  



在hdfs上建立 /examples 文件夹
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. sudo -u hdfs hdfs dfs -mkdir /examples  

把examples 文件夹里面的  src\main\apps  文件夹传到这个文件夹底下
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -put examples/src/main/apps /examples  

建立输出跟输入文件夹并上传测试数据
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -mkdir -p /user/root/examples/input-data/text  
  2. hdfs dfs -mkdir -p /user/root/examples/output-data  
  3. hdfs dfs -put oozie-examples/src/main/data/data.txt /user/root/examples/input-data/text  

Step4

运行这个任务
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. oozie job -oozie http://host1:11000/oozie -config oozie-examples/src/main/apps/map-reduce/job.properties -run  

任务创建成功后会返回一个job号比如 job: 0000017-150302164219871-oozie-oozi-W

然后你可以采用之前提供的 3 个连接oozie 的方法去查询任务状态,这里我采用HUE去查询的情况,点击最上面的 Workflow -> 仪表盘 -> Workflow


会看到有一个任务正在运行

点击后,可以实时的看任务状态,完成后会变成SUCCESS

这时候去看下结果 /user/root/examples/output-data/map-reduce/part-00000
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 0   To be or not to be, that is the question;  
  2. 42  Whether 'tis nobler in the mind to suffer  
  3. 84  The slings and arrows of outrageous fortune,  
  4. 129 Or to take arms against a sea of troubles,  
  5. 172 And by opposing, end them. To die, to sleep;  
  6. 217 No more; and by a sleep to say we end  
  7. 255 The heart-ache and the thousand natural shocks  
  8. 302 That flesh is heir to ? 'tis a consummation  
  9. 346 Devoutly to be wish'd. To die, to sleep;  
  10. 387 To sleep, perchance to dream. Ay, there's the rub,  
  11. 438 For in that sleep of death what dreams may come,  
  12. 487 When we have shuffled off this mortal coil,  
  13. 531 Must give us pause. There's the respect  
  14. 571 That makes calamity of so long life,  
  15. 608 For who would bear the whips and scorns of time,  
  16. 657 Th'oppressor's wrong, the proud man's contumely,  
  17. 706 The pangs of despised love, the law's delay,  
  18. 751 The insolence of office, and the spurns  
  19. 791 That patient merit of th'unworthy takes,  
  20. 832 When he himself might his quietus make  
  21. 871 With a bare bodkin? who would fardels bear,  
  22. 915 To grunt and sweat under a weary life,  
  23. 954 But that the dread of something after death,  
  24. 999 The undiscovered country from whose bourn  
  25. 1041    No traveller returns, puzzles the will,  
  26. 1081    And makes us rather bear those ills we have  
  27. 1125    Than fly to others that we know not of?  
  28. 1165    Thus conscience does make cowards of us all,  
  29. 1210    And thus the native hue of resolution  
  30. 1248    Is sicklied o'er with the pale cast of thought,  
  31. 1296    And enterprises of great pitch and moment  
  32. 1338    With this regard their currents turn awry,  
  33. 1381    And lose the name of action.  

workflow.xml解析

我们把刚刚这个例子里面的workflow.xml打开看下
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <workflow-app xmlns="uri:oozie:workflow:0.2" name="map-reduce-wf">  
  2.     <start to="mr-node"/>  
  3.     <action name="mr-node">  
  4.         <map-reduce>  
  5.             <job-tracker>${jobTracker}</job-tracker>  
  6.             <name-node>${nameNode}</name-node>  
  7.             <prepare>  
  8.                 <delete path="${nameNode}/user/${wf:user()}/${examplesRoot}/output-data/${outputDir}"/>  
  9.             </prepare>  
  10.             <configuration>  
  11.                 <property>  
  12.                     <name>mapred.job.queue.name</name>  
  13.                     <value>${queueName}</value>  
  14.                 </property>  
  15.                 <property>  
  16.                     <name>mapred.mapper.class</name>  
  17.                     <value>org.apache.oozie.example.SampleMapper</value>  
  18.                 </property>  
  19.                 <property>  
  20.                     <name>mapred.reducer.class</name>  
  21.                     <value>org.apache.oozie.example.SampleReducer</value>  
  22.                 </property>  
  23.                 <property>  
  24.                     <name>mapred.map.tasks</name>  
  25.                     <value>1</value>  
  26.                 </property>  
  27.                 <property>  
  28.                     <name>mapred.input.dir</name>  
  29.                     <value>/user/${wf:user()}/${examplesRoot}/input-data/text</value>  
  30.                 </property>  
  31.                 <property>  
  32.                     <name>mapred.output.dir</name>  
  33.                     <value>/user/${wf:user()}/${examplesRoot}/output-data/${outputDir}</value>  
  34.                 </property>  
  35.             </configuration>  
  36.         </map-reduce>  
  37.         <ok to="end"/>  
  38.         <error to="fail"/>  
  39.     </action>  
  40.     <kill name="fail">  
  41.         <message>Map/Reduce failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>  
  42.     </kill>  
  43.     <end name="end"/>  
  44. </workflow-app>  

最重要的就是里面的action 节点。
中间那段action 可以有支持几种类型的action
  • Map-Reduce Action
  • Pig Action
  • Fs(HDFS) Action
  • Java Action
  • Email Action
  • Shell Action
  • Hive Action
  • Sqoop Action
  • Ssh Action
  • DistCp Action
  • 自定义Action
  • sub-workflow (这个可以嵌套另外一个workflow.xml文件的路径)
具体见 http://oozie.apache.org/docs/4.1.0/WorkflowFunctionalSpec.html#a3.2_Workflow_Action_Nodes  


这个简单的map-reduce 其实什么也没干,只是把文本一行的读取并打印出来。接下来我要把这个例子改成我们熟悉的WordCount例子

WordCount例子

Step1

先改一下我们的Mapper 和 Reducer 代码
修改SampleMapper为
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package org.apache.oozie.example;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.StringTokenizer;  
  5.   
  6. import org.apache.hadoop.io.IntWritable;  
  7. import org.apache.hadoop.io.LongWritable;  
  8. import org.apache.hadoop.io.Text;  
  9. import org.apache.hadoop.mapreduce.Mapper;  
  10.   
  11.   
  12. public class SampleMapper extends Mapper<LongWritable, Text, Text, IntWritable> {  
  13.   
  14.     private final static IntWritable one = new IntWritable(1);  
  15.     private Text word = new Text();  
  16.   
  17.   
  18.     public void map(LongWritable key, Text value, Context context)  
  19.             throws IOException, InterruptedException {  
  20.   
  21.         String line = value.toString();  
  22.         StringTokenizer tokenizer = new StringTokenizer(line);  
  23.   
  24.         while (tokenizer.hasMoreTokens()) {  
  25.             word.set(tokenizer.nextToken());  
  26.             context.write(word, one);  
  27.         }  
  28.     }  
  29.   
  30. }  

然后再把Reducer修改为
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package org.apache.oozie.example;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.IntWritable;  
  6. import org.apache.hadoop.io.Text;  
  7. import org.apache.hadoop.mapreduce.Reducer;  
  8.   
  9. public class SampleReducer extends Reducer<Text, IntWritable, Text, IntWritable> {  
  10.   
  11.     public void reduce(Text key, Iterable<IntWritable> values, Context context)  
  12.             throws IOException, InterruptedException {  
  13.   
  14.         int sum = 0;  
  15.   
  16.         for (IntWritable val : values) {  
  17.             sum += val.get();  
  18.         }  
  19.   
  20.         context.write(key, new IntWritable(sum));  
  21.     }  
  22.   
  23. }  

改好后用 mvn clean package 打包好,还是上传到 /user/root/examples/apps/map-reduce/lib 覆盖之前的那份jar


这边说一点题外话,关于MapReduce的old API跟new API的区别,这个跟我们这次的教程没关系,如果不感兴趣的同学可以直接跳过下面这一段

MapReduce 的 old API 跟 new API 区别

mapreduce  分为 old api 和 new  api , new api废弃了 org.apache.hadoop.mapred 包下的 Mapper 和 Reducer,新增了org.apache.hadoop.mapreduce包,如果你手头有用旧api写的mp(mapreduce)任务可以通过以下几个改动修改为新的mp写法
  • 将implements Mapper/Reducer 改为 extends Mapper/Reducer,因为new API 里 Mapper 和 Reducer不是接口,并且包的位置变成 org.apache.hadoop.mapreduce.Mapper
  • OutputCollector 改为 Context
  • map方法改成 map(LongWritable key, Text value, Context context)  reduce 方法改成 
 具体见  Hadoop WordCount with new map reduce api  


Step2

我们把之前的 src/main/apps/map-reduce/workflow.xml 修改一下成为这样
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <workflow-app xmlns="uri:oozie:workflow:0.2" name="map-reduce-wf">  
  2.     <start to="mr-node"/>  
  3.     <action name="mr-node">  
  4.         <map-reduce>  
  5.             <job-tracker>${jobTracker}</job-tracker>  
  6.             <name-node>${nameNode}</name-node>  
  7.             <prepare>  
  8.                 <delete path="${nameNode}/user/${wf:user()}/${examplesRoot}/output-data/${outputDir}"/>  
  9.             </prepare>  
  10.             <configuration>  
  11.               
  12.                 <property>  
  13.                     <name>mapred.mapper.new-api</name>  
  14.                     <value>true</value>  
  15.                 </property>  
  16.                 <property>  
  17.                     <name>mapred.reducer.new-api</name>  
  18.                     <value>true</value>  
  19.                 </property>  
  20.               
  21.                 <property>  
  22.                     <name>mapred.output.key.class</name>  
  23.                     <value>org.apache.hadoop.io.Text</value>  
  24.                 </property>  
  25.                 <property>  
  26.                     <name>mapred.output.value.class</name>  
  27.                     <value>org.apache.hadoop.io.IntWritable</value>  
  28.                 </property>  
  29.                   
  30.                 <property>  
  31.                     <name>mapreduce.inputformat.class</name>  
  32.                     <value>org.apache.hadoop.mapreduce.lib.input.TextInputFormat</value>  
  33.                 </property>  
  34.                 <property>  
  35.                     <name>mapreduce.outputformat.class</name>  
  36.                     <value>org.apache.hadoop.mapreduce.lib.output.TextOutputFormat</value>  
  37.                 </property>  
  38.                
  39.                 <property>  
  40.                     <name>mapred.job.queue.name</name>  
  41.                     <value>${queueName}</value>  
  42.                 </property>  
  43.                 <property>  
  44.                     <name>mapreduce.map.class</name>  
  45.                     <value>org.apache.oozie.example.SampleMapper</value>  
  46.                 </property>  
  47.                 <property>  
  48.                     <name>mapreduce.reduce.class</name>  
  49.                     <value>org.apache.oozie.example.SampleReducer</value>  
  50.                 </property>  
  51.                 <property>  
  52.                     <name>mapred.map.tasks</name>  
  53.                     <value>1</value>  
  54.                 </property>  
  55.                 <property>  
  56.                     <name>mapred.input.dir</name>  
  57.                     <value>/user/${wf:user()}/${examplesRoot}/input-data/text</value>  
  58.                 </property>  
  59.                 <property>  
  60.                     <name>mapred.output.dir</name>  
  61.                     <value>/user/${wf:user()}/${examplesRoot}/output-data/${outputDir}</value>  
  62.                 </property>  
  63.             </configuration>  
  64.         </map-reduce>  
  65.         <ok to="end"/>  
  66.         <error to="fail"/>  
  67.     </action>  
  68.     <kill name="fail">  
  69.         <message>Map/Reduce failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>  
  70.     </kill>  
  71.     <end name="end"/>  
  72. </workflow-app>  

我把中间的action 里面的属性替换了,我说明一下几个重要属性
  • mapred.mapper.new-api  和 mapred.reducer.new-api 意思是是否要使用new API,我们这边设置为true
  • mapred.output.key.class 和 mapred.output.value.class 意思是 mapper的输出类型
  • mapreduce.map.class 和 mapreduce.reduce.class 这两处连属性名都修改了,可能很多人会发现不了,之前是 mapred.mapper.class 和 mapred.reducer.class ,如果你只改了value就会出错,说new API的属性里面没有这两个属性
然后我们把workflow.xml上传到hdfs上
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -put -f oozie-examples/src/main/apps/map-reduce/workflow.xml /user/root/examples/apps/map-reduce/  

Step3 

我们把素材准备一下,还是之前做 wordcount 用的 file0 和 file1
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. $ echo "Hello World Bye World" > file0  
  2. $ echo "Hello Hadoop Goodbye Hadoop" > file1  
  3. $ hdfs dfs -put file* /user/root/examples/input-data/text  
顺便把之前的data.txt删掉
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. hdfs dfs -rm /user/root/examples/input-data/text/data.txt  

Step4

我们来运行一下这个job

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. oozie job -oozie http://host1:11000/oozie -config oozie-examples/src/main/apps/map-reduce/job.properties -run  

执行完后到 / user/ root/ examples/ output-data/ map-reduce/ part-r-00000 查看我们的结果
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Bye 1  
  2. Goodbye 1  
  3. Hadoop  2  
  4. Hello   2  
  5. World   2  

完成!

0 0