最近在项目中有用到使用sqoop将结构化数据上传到hadoop的hdfs文件系统上,然后通过oozie进行定时调度,期中碰见了一些坑,将其记录下来。在sqoop将数据导入进hdfs上,首先我们需要做的是将源数据库的jdbc驱动添加进oozie的sqoop的lib目录下,因为sqoop导入数据,实质上是通过与数据库建立一个jdbc连接,然后再进行 数据的导入的。一般oozie的lib库在安装时,会默认上传到hdfs文件系统上的,而且放置的问题为hdfs的根目录下 /user/oozie/share/lib
如果你在hdfs文件系统上没有找到lib库,那么你就需要自己上传了。首先你需要找到oozie的安装目录。
找到oozie-sharelib.tar.gz文件,将其解压出来,并且上传到hdfs文件系统上。
tar -zvxf oozie-sharelib.tar.gz
解压后,你可以把jdbc的驱动包放进sqoo的目录下面再上传到
最后在把这些包上传到hdfs上
hadoop fs -put -f 本地资源路径 hdfs存放资源路径
接下就是oozie对sqoop的调度了,oozie是一个很强大的调度工具,因为它有对shell操作的action,那么就决定了它可以实现很多操作,同时它也有sqoop的action,我们可以直接使用sqoop的action来完成我们需要的调度任务
一个基本的定时调度,它应该包括三个文件,一个放置配置变量文件 job.properties,一个用来定时调度的调度文件 coordinator.xml,一个用来具体抽取数据的工作流文件 workflow.xml,
job.properties
nameNode=hdfs://master11:8020 #nomenode的地址和端口
jobTracker=master11:8050 #MapReduce的地址
queueName=oozie #该调度由哪个资源队列来完成
examplesRoot=jobwork/all/ #设置一个路径变量
oozie.libpath=/user/oozie/share/lib #ooize调度jar包资源路径
oozie.use.system.libpath=true #是否使用oozie自带的jar资源
oozie.coord.application.path=${nameNode}/${examplesRoot}/ext_All_coordinator.xml #定时调度的文件路径
ext_bcdf_stationCar_workflow=${nameNode}/${examplesRoot}/ext_All_workflow.xml #工作流路径
start=2018-12-12T16:10+0800 #设置一个开始调度时间
end=2098-08-20T00:00+0800 #设置一个结束
coordinate.xml
<coordinator-app name="cron-coord" frequency="${coord:minutes(15)}" start="${start}" end="${end}" timezone="GMT+0800"
xmlns="uri:oozie:coordinator:0.2">
<!--
frequency 设置调度的时间频率,我这里表示15分钟调度一次
start 设置调度开始时间
end 设置调度结束时间
timezone 设置时区
-->
<action>
<workflow>
<app-path>${ext_bcdf_stationCar_workflow}</app-path>
<configuration>
<property>
<name>jobTracker</name>
<value>${jobTracker}</value>
</property>
<property>
<name>nameNode</name>
<value>${nameNode}</value>
</property>
<property>
<name>queueName</name>
<value>${queueName}</value>
</property>
<property>
<name>last_time</name>
<value>${coord:formatTime(coord:dateOffset(coord:nominalTime(), -15, 'MINUTE'), 'yyyy-MM-dd HH:mm:ss')}</value><!--获取一个脚本当前执行前15分钟的时间,并格式化我想要的格式-->
</property>
<property>
<name>current_time</name>
<value>${coord:formatTime(coord:dateOffset(coord:nominalTime(), 0, 'MINUTE'), 'yyyy-MM-dd HH:mm:ss')}</value> <!--获取一个脚本当前执行时间,并格式化我想要的格式-->
</property>
</configuration>
</workflow>
</action>
</coordinator-app>
workflow.xml
<workflow-app xmlns="uri:oozie:workflow:0.4" name="ext_allinsert2">
<start to="extract_stationcar"/>
<action name="extract_stationcar">
<sqoop xmlns="uri:oozie:sqoop-action:0.2">
<job-tracker>${jobTracker}</job-tracker>
<name-node>${nameNode}</name-node>
<configuration>
<property>
<name>mapred.job.queue.name</name>
<value>${queueName}</value>
</property>
</configuration>
<arg>import</arg>
<arg>--connect</arg>
<arg>jdbc:oracle:thin:@//*:1521/dzsw </arg>
<arg>--query</arg>
<arg>SELECT *FROM STATIONCAR where $CONDITIONS </arg>
<arg>--username</arg>
<arg>xc</arg>
<arg>--password</arg>
<arg>xc123 </arg>
<arg>--target-dir</arg>
<arg>/data/integration/ext_stationcar</arg>
<arg>-m</arg>
<arg>1</arg>
<arg>--fields-terminated-by </arg>
<arg>\\</arg>
<arg>--delete-target-dir</arg>
</sqoop>
<ok to="end" />
<error to="fail" />
</action>
<kill name="fail">
<message>[${wf:errorMessage(wf:lastErrorNode())}]</message>
</kill>
<end name="end"/>
</workflow-app>
以上三个文件就能完成每十五分钟从数据库里面抽取数据到hdfs文件系统上面了。
使用需要注意的地方
1.action之间不能间接或者直接指向同一个action,比如action1执行成功指向action2,aciton1执行失败指向aciton3,然后action2执行成功后指向action3,这样是不被允许的,只有end和fail是可以被重复指向的,其余所有类型的action都不能这么使用,不然会类似下面这种错误
2.Ozoie在调度时,遇见脚本被kill,然后查看日志,发现下面这个问题
No lease on /user/hadoop/oozie-oozi/0000092-181218090347177-oozie-oozi-W/extract_stationcar--sqoop/0000092-181218090347177-oozie-oozi-W@extract_stationcar@0 (inode 1058886)
这个问题咋一看可能会会认为就是因为找不到这个文件而引起的,但是后面又想想我这是单线程跑的,不可能出现别的线程将该文件删除的情况,而且这个脚本之前还能跑的很好,最重要的是这个报错信息一下是摸不着头脑的,最后只能接着往下找,最终让我找到了
The Network Adapter could not establish the connection
很明显这个才是脚本被kill掉的最终原因,从而引发了上面那个问题。