oozie简介_Oozie简介

oozie简介

在Hadoop中执行的任务有时需要将多个Map / Reduce作业链接在一起以完成其目标。 [1]在Hadoop生态系统中,有一个相对较新的组件Oozie [2],该组件允许将多个Map / Reduce作业组合成一个逻辑工作单元,从而完成更大的任务。 在本文中,我们将介绍Oozie及其使用方式。

什么是Oozie?

Oozie是一个Java Web应用程序,它在Java servlet容器Tomcat中运行,并使用数据库来存储:

  • 工作流程定义
  • 当前正在运行的工作流实例,包括实例状态和变量

Oozie工作流是在控件依赖项DAG(直接非循环图)中排列的一系列动作(即Hadoop Map / Reduce作业,Pig作业)的集合,指定了一系列动作执行。 此图以hPDL(一种XML流程定义语言)指定。

hPDL是一种相当紧凑的语言,使用了数量有限的流控制和动作节点。 控制节点定义执行流程,包括工作流的开始和结束(开始,结束和失败节点)和控制工作流执行路径的机制(决策,派生和联接节点)。 动作节点是一种机制,工作流通过该机制触发计算/处理任务的执行。 Oozie为以下类型的操作提供支持:Hadoop映射减少,Hadoop文件系统,Pig,Java和Oozie子工作流(从Oozie模式0.2开始删除了SSH操作)。

由动作节点触发的所有计算/处理任务都在Oozie的远端-它们由Hadoop Map / Reduce框架执行。 这种方法允许Oozie利用现有的Hadoop机械进行负载平衡,故障转移等。这些任务中的大多数都是异步执行的(例外是同步处理的文件系统操作)。 这意味着对于由工作流操作触发的大多数类型的计算/处理任务,工作流作业必须等到计算/处理任务完成后才能转换到工作流中的下一个节点。 Oozie可以通过两种不同的方法(回调和轮询)来检测计算/处理任务的完成情况。 当Oozie启动计算/处理任务时,Oozie将为任务提供唯一的回调URL,该任务应调用给定的URL来通知其完成。 对于任务由于任何原因未能调用回调URL(例如,短暂的网络故障)或任务类型在完成时无法调用回调URL的情况,Oozie具有轮询完成的计算/处理任务的机制。

可以对Oozie工作流进行参数化(使用工作流定义中的$ {inputDir}之类的变量)。 提交工作流时,必须提供参数的作业值。 如果正确设置了参数(即使用不同的输出目录),则可以同时执行几个相同的工作流程作业。

有些工作流是按需调用的,但大多数情况下有必要根据规则的时间间隔和/或数据可用性和/或外部事件来运行它们。 Oozie Coordinator系统允许用户基于这些参数定义工作流程执行时间表。 Oozie协调器允许以谓词形式对工作流执行触发器进行建模,该谓词可以引用数据,时间和/或外部事件。 满足谓词后,将启动工作流程作业。

通常还需要连接定期运行但以不同时间间隔运行的工作流作业。 工作流的多个后续运行的输出将成为下一个工作流的输入。 将这些工作流链接在一起将其称为数据应用程序管道。 Oozie协调器支持创建此类数据应用程序管道。

安装Oozie

可以通过tarball,RPM或Debian软件包将Oozie安装在现有的Hadoop系统上。 我们的Hadoop安装是Cloudera的CDH3,其中已经包含Oozie。 结果,我们仅使用yum将其拉低并在边缘节点[1]上执行安装。Oozie发行版中有两个组件-Oozie-client和Oozie-server。 根据群集的大小,两个组件可能都位于同一台边缘服务器上或在不同的计算机上。 Oozie服务器包含用于启动和控制作业的组件,而客户端包含用于人员能够启动Oozie作业并与Oozie服务器通信的组件。

有关安装过程的更多信息,请使用Cloudera发行版,请访问Cloudera网站[2]。

注意:除了安装过程外,建议添加外壳变量OOZIE_URL

(export OOZIE_URL=http://localhost:11000/oozie)

到您的.login,.kshrc或您选择的Shell启动文件中。

一个简单的例子

为了展示Oozie的用法,我们来看一个简单的例子。 我们有两个Map / Reduce作业[3] -一个正在执行数据的初始提取,第二个正在合并给定类型的数据。 实际摄取需要执行初始摄取,然后合并两种类型的数据-Lidar和Multicam。 为了使这一过程自动化,我们创建了一个简单的Oozie工作流(清单1)。

<!--
Copyright (c) 2011 NAVTEQ! Inc. All rights reserved.
NGMB IPS ingestor Oozie Script
-->
<workflow-app xmlns= 'uri:oozie:workflow:0.1'  name= 'NGMB-IPS-ingestion' >
 <start to = 'ingestor' />
 <action name = 'ingestor' >
  <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> com.navteq.assetmgmt.MapReduce.ips.IPSLoader </main-class>
                    <java-opts> -Xmx2048m </java-opts>
   <arg> ${driveID} </arg>
  </java>
 <ok to = "merging" />
 <error to = "fail" />
 </action>
  <fork name = "merging" >
   <path start = "mergeLidar" />
   <path start = "mergeSignage" />
  </fork>
 <action name = 'mergeLidar' >
  <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> com.navteq.assetmgmt.hdfs.merge.MergerLoader </main-class>
                    <java-opts> -Xmx2048m </java-opts>
   <arg> -drive </arg>
   <arg> ${driveID} </arg>
   <arg> -type </arg>
   <arg> Lidar </arg>
   <arg> -chunk </arg>
   <arg> ${lidarChunk} </arg>
  </java>
  <ok to = "completed" />
  <error to = "fail" />
 </action>
 <action name = 'mergeSignage' >
  <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> com.navteq.assetmgmt.hdfs.merge.MergerLoader </main-class>
                    <java-opts> -Xmx2048m </java-opts>
   <arg> -drive </arg>
   <arg> ${driveID} </arg>
   <arg> -type </arg>
   <arg> MultiCam </arg>
   <arg> -chunk </arg>
   <arg> ${signageChunk} </arg>
  </java>
  <ok to = "completed" />
  <error to = "fail" />
 </action>
 <join name = "completed" to = "end" />
 <kill name = "fail" >
  <message> Java failed, error message[${wf:errorMessage(wf:lastErrorNode())}] </message>
 </kill>
 <end name = 'end' />
</workflow-app>  

清单1:简单的Oozie工作流程

该工作流程定义了3个动作-接收器,mergeLidar和mergeSignage。 每个动作都作为Map / Reduce [4]作业实现。 工作流程从起始节点开始,该起始节点将控制权转移到Ingestor操作。 摄取器步骤完成后,将调用派生控制节点[4],它将并行执行[5]并执行mergeLidar和mergeSignage。 一旦两个动作完成,就调用加入控制节点[5] [6] 。 成功完成加入节点后,控件将传递到结束节点,结束该过程。

创建工作流程后,必须正确部署它。 一个典型的Oozie部署是一个HDFS目录,其中包含工作流.xml(清单1),config-default.xml和一个lib子目录,其中包含工作流操作使用的类的jar文件。

(点击图片放大。)

图1:Oozie部署

config-default.xml文件是可选的,并且通常包含所有工作流程实例共有的工作流程参数。 清单2给出了一个config-default.xml的简单示例。

<configuration>
    <property>
        <name> jobTracker </name>
        <value> sachicn003:2010 </value>
    </property>
    <property>
        <name> nameNode </name>
        <value> hdfs ://sachicn001:8020 </value>
    </property>
    <property>
        <name> queueName </name>
        <value> default </value>
    </property>
</configuration> 

清单2:Config-default.xml

一旦部署了工作流,Oozie提供的命令行实用程序[5]可用于提交,启动和操作工作流。 该实用程序通常在Hadoop群集[7]的边缘节点上运行,并且需要一个作业属性文件(请参见侧栏–配置工作流属性)-清单3。

oozie.wf.application.path=hdfs ://sachicn001:8020/user/ blublins / workflows /IPSIngestion
jobTracker= sachicn003:2010
nameNode= hdfs ://sachicn001:8020 

清单3:作业属性文件

有了作业属性,清单4中显示的命令可用于运行Oozie工作流程。

oozie job –oozie http://sachidn002.hq.navteq.com:11000/oozie/ -D driveID=729-pp00002-2011-02-08-09-59-34 -D lidarChunk=4 -D signageChunk=20 -config job.properties –run

清单4:运行工作流命令

配置工作流属性

config-default.xml,作业属性文件和作业参数之间存在一些重叠,这些重叠可以作为命令行调用的一部分传递给Oozie。 尽管文档中没有明确说明何时使用哪种工具,但总体建议如下:

  • 使用config-default.xml定义对于给定工作流程永远不变的参数
  • 将作业属性用于工作流的给定部署所共有的参数
  • 使用命令行参数作为特定于给定工作流程调用的参数。

Oozie处理这三组参数的方式如下:

  • 使用命令行调用中的所有参数
  • 如果那里的参数无法解析,请尝试使用job config解析它们
  • 一旦其他所有操作失败,请尝试使用config-default.xml

Oozie控制台(图2)可用于观察工作流执行的进度和结果。

(点击图片放大。)

图2:Oozie控制台

Oozie控制台还可用于获取作业执行的详细信息,例如作业日志[8] (图3)

(点击图片放大。)

图3:Oozie控制台–作业日志

程序化工作流程调用

尽管上面描述的命令行界面对于Oozie的手动调用非常有效,但是以编程方式调用Oozie有时是有利的。 当Oozie工作区是特定应用程序的一部分或大型企业流程的一部分时,这很有用。 可以使用Oozie Web服务API [6]或Oozie Java客户端API [7]来实现这样的程序调用。 清单5展示了一个非常简单的上述过程的Oozie java客户端调用。

package com.navteq.assetmgmt.oozie;

import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.OozieClientException;
import org.apache.oozie.client.WorkflowJob;
import org.apache.oozie.client.WorkflowJob.Status;

public class WorkflowClient {

         private static String OOZIE_URL = "http://sachidn002.hq.navteq.com:11000/oozie/";
         private static String JOB_PATH = "hdfs://sachicn001:8020/user/blublins/workflows/IPSIngestion";
         private static String JOB_Tracker = "sachicn003:2010";
         private static String NAMENode = "hdfs://sachicn001:8020";

         OozieClient wc = null ;

         public WorkflowClient(String url){

                         wc = new OozieClient(url);
                 }

                 public String startJob(String wfDefinition, List<WorkflowParameter> wfParameters)
                                                                                throws OozieClientException{

                          // create a workflow job configuration and set the workflow application path
                  Properties conf = wc .createConfiguration();
                  conf.setProperty(OozieClient. APP_PATH , wfDefinition);

                  // setting workflow parameters
                  conf.setProperty ( "jobTracker", JOB_Tracker );
                  conf.setProperty( "nameNode", NAMENode );
                  if ((wfParameters != null ) && (wfParameters.size() > 0)){
                          for (WorkflowParameter parameter : wfParameters)
                           conf.setProperty(parameter.getName(), parameter.getValue());
                  }
                  // submit and start the workflow job
                  return wc .run(conf);
                 }

                 public Status getJobStatus(String jobID) throws OozieClientException{

                          WorkflowJob job = wc .getJobInfo(jobID);
                          return job.getStatus();
                 }

                 public static void main(String[] args) throws OozieClientException, InterruptedException{

                          // Create client
                          WorkflowClient client = new WorkflowClient( OOZIE_URL );
                          // Create parameters
                          List<WorkflowParameter> wfParameters = new LinkedList<WorkflowParameter>();
                          WorkflowParameter drive = new WorkflowParameter( "driveID","729-pp00004-2010-09-01-09-46" );
                          WorkflowParameter lidar = new WorkflowParameter ( "lidarChunk","4" );
                          WorkflowParameter signage = new WorkflowParameter( "signageChunk","4" );
                          wfParameters.add(drive);
                          wfParameters.add(lidar);
                          wfParameters.add(signage);
                          // Start Oozing
                          String jobId = client.startJob( JOB_PATH , wfParameters);
                          Status status = client.getJobStatus(jobId);
                          if (status == Status. RUNNING )
                                     System. out .println( "Workflow job running" );
                          else
                                     System. out .println( "Problem starting Workflow job" );
                  }
       }

清单5:简单的Oozie Java客户端

在这里,第一个工作流程客户端使用Oozie服务器URL初始化。 初始化完成后,可以使用此客户端提交和启动作业(startJob方法),获取正在运行的作业的状态(getStatus方法)和其他操作。

构建Java操作,将参数传递给工作流

到目前为止,在我们的示例中,我们已经展示了如何使用<arg>标签将参数传递给Java节点。 考虑到Java节点是将自定义计算引入Oozie的主要方法,因此能够将数据从Java节点传递到Oozie也很重要。

根据Java节点文档[3],“ capture-output”元素可用于将值从Java节点传播回Oozie上下文。 然后,可以通过工作流程中的其他步骤通过EL功能访问这些值。 返回值需要作为Java属性格式文件写出。 可以通过常量“ JavaMainMapper.OOZIE_JAVA_MAIN_CAPTURE_OUTPUT_FILE”指定的System属性获得此类属性文件的名称。清单6给出了演示如何执行此操作的简单示例。

package com.navteq.oozie;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Properties;

public class GenerateLookupDirs {

        /**
        * @param args
        */
        public static final long dayMillis = 1000 * 60 * 60 * 24;
        private static final String OOZIE_ACTION_OUTPUT_PROPERTIES = "oozie.action.output.properties";

        public static void main(String[] args) throws Exception {
                Calendar curDate = new GregorianCalendar();
                int year, month, date;
                String propKey, propVal;

                String oozieProp = System.getProperty(OOZIE_ACTION_OUTPUT_PROPERTIES);
                if (oozieProp != null) {
                         File propFile = new File(oozieProp);
                         Properties props = new Properties();

                         for (int i = 0; I < 8; ++i)
                         {
                                    year = curDate.get(Calendar.YEAR);
                                    month = curDate.get(Calendar.MONTH) + 1;
                                    date = curDate.get(Calendar.DATE);
                                    propKey = "dir"+i;
                                    propVal = year + "-" +
                                            (month < 10 ? "0" + month : month) + "-" +
                                            (date < 10 ? "0" + date : date);
                                    props.setProperty(propKey, propVal);
                                    curDate.setTimeInMillis(curDate.getTimeInMillis() - dayMillis);
                         }
                         OutputStream os = new FileOutputStream(propFile);
                         props.store(os, "");
                         os.close();
                 } else
                         throw new RuntimeException(OOZIE_ACTION_OUTPUT_PROPERTIES
                                        + " System property not defined");
        }
}

清单6:将参数传递给Oozie

在此示例中,我们假设HDFS中每天都有一个目录。 因此,此类首先获取当前日期,然后计算前7个最近的日期(包括当前日期),并将目录名称传递回Oozie。

结论

在本文中,我们介绍了Oozie(一种用于Hadoop的工作流引擎),并提供了其用法的简单示例。 在下一篇文章中,我们将看一个更复杂的示例,使我们可以讨论更多Oozie功能。

致谢

作者感谢我们的Navteq同事Gregory Titievsky提供了一些示例。

关于作者

Boris Lublinsky是NAVTEQ的首席架构师,在那里他致力于为大数据管理和处理以及SOA定义体系结构远景,并实施各种NAVTEQ项目。 他还是InfoQ的SOA编辑器,并且是OASIS中SOA RA工作组的参与者。 鲍里斯(Boris)是他的最新著作《应用的SOA》的作者兼经常演讲者。

迈克尔·塞格尔(Michael Segel )在过去20多年中一直与客户合作,以​​识别和解决他们的业务问题。 迈克尔曾在多个行业担任过多个职务。 他是一位独立顾问,他一直致力于解决任何具有挑战性的问题。 Michael拥有俄亥俄州立大学的软件工程学位。


[1]边缘节点是一台安装了Hadoop库,但不属于实际集群的计算机。 它用于能够连接到群集并托管辅助服务和直接访问群集的最终用户应用程序的应用程序。

[2] 有关Oozie的安装,请参见此链接。

[3]这些工作的细节与本文无关,因此我们不在此介绍

[4]可以通过两种不同的方式在Oozie中实现Map / Reduce作业–作为真正的Map / Reduce动作[2],您可以在其中指定Mapper和Reducer类及其配置,也可以作为Java动作[3],在其中指定类正在使用Hadoop API启动Map / Reduce作业。 因为我们已经拥有所有使用Hadoop API并实现一些其他功能的Java主干,所以我们选择了第二种方法。

[5] Oozie保证将两个操作同时提交给Job Tracker。 执行中的实际并行性不在Oozie的控制范围内,并取决于作业要求,集群容量和Map / Reduce安装所使用的调度程序。

[6] join动作的作用是同步由fork动作启动的并行执行的多个线程。 如果由fork启动的所有执行线程成功完成,则join操作正在等待所有执行线程完成。 如果至少在执行线程上失败,则kill节点将“杀死”剩余的正在运行的线程。

[7]此节点不必是已安装Oozie的节点。

[8]虽然Oozie作业日志包含工作流执行的详细信息,但是要查看操作执行的详细信息,有必要切换到Hadoop Map / Reduce Administration页面。

翻译自: https://www.infoq.com/articles/introductionOozie/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

oozie简介

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值