Hadoop Streaming介绍
0x01 基础概念
1.1 简介
用户可以使用Hadoop Streaming来用任意语言(如python)来编写、运行MR作业,下面是一个官方示例:
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
-input myInputDirs \
-output myOutputDir \
-mapper /bin/cat \
-reducer /bin/wc
上面的例子中的mapper
和reducer
都是可执行的文件,他们从标准输入stdin
中按行读取输入然后将输出发送到标准输出stdout
中。随后Hadoop Streaming会创建一个MR作业,提交该任务到集群并持续监控直到任务完成。
1.2 原理
首先上一张HadoopStreaming的运行流程图:
1.2.1 Mapper
当一个可执行包被指定为mapper
时,每个mapper任务在初始化时会以单独的进程的方式来执行该程序包。在mapper任务运行后会将会把自己的input
转化为多行然后喂给前面说的进程的stdin
。同时,mapper会从该进程的stdout
中搜集面向行的output
并将每一行转化为<key,value>
的键值对,将他们作为该mapper的输出。
默认情况下,一行数据从开始到第一个制表符(\t)前视作key
,其余部分(不包括制表符)作为value
。也就是说如果一行中没有制表符,那么key就是整行,value为null。当然,这是可配的。
1.2.2 Reducer
当一个可执行包被指定为reducer
时,每个reducer任务在初始化时会以单独的进程的方式来执行该程序包。在reducer任务运行后会将会把自己的<key,value>
形式的input
转化为多行然后喂给前面说的进程的stdin
。同时,reducer会从该进程的stdout
中搜集面向行的output
并将每一行转化为<key,value>
的键值对,将他们作为该reducer的输出。
跟mapper一样,在默认情况下,一行数据从开始到第一个制表符(\t)前视作key
,其余部分(不包括制表符)作为value
。也就是说如果一行中没有制表符,那么key就是整行,value为null。当然,这也是可配的。
1.3 缺点
需要注意的是,HadoopStreming也有一些缺点:
- 默认只能处理文本数据,无法直接对二进制数据进行处理;
- Streaming中的mapper和reducer默认只能从stdin和stdout输入输出
- 因为有中间转换过程,相对于原生的java mr来说会有一定的性能开销
0x02 基础参数
作为python程序员,了解Hadoop Streaming的常用参数含义是十分有必要的。
通常来说,一个Hadoop Streaming程序的提交命令格式如下:
hadoop command [genericOptions] [streamingOptions]
注意,通用命令选项必须放在 Streaming Command Options的前面,否则会导致失败。
2.1 Generic Command Options-通用命令选项
2.1.1 D
选填;-D property=value一般是用来设mr任务的一些属性值
# 指定map任务数量,默认是2,用于在很小的任务时配置
-D mapreduce.job.maps=1
# 指定reduce任务数量。以下参数就是指定不运行reduce任务
-D mapreduce.job.reduces=0
2.1.2 files
选填;该参数是URI,指向已经被上传到HDFS的文件。这些文件会可跨作业缓存。多个文件以逗号分隔。任务可以通过该参数来使用files。
-files
会在任务的当前工作目录中创建符号链接(软连接),指向该文件的本地副本。
下面是一个示例:
-files hdfs://host:fs_port/data/a.txt,hdfs://host:fs_port/data/b.txt \
-mapper "python test.py a.txt b.txt"
上面这条命令会让Hadoop在当前任务的工作空间目录中自动创建名为a.txt
和b.txt
的软连接,分别指向这两个文件在本地的副本。
如果你想重命名软连接,可以使用如下命令:
-files hdfs://host:fs_port/user/a.txt#customA
-mapper "python test.py customA"
2.1.3 archives
选填;该参数是URI,指向已经被上传到HDFS的archive file
。这些archive file会可跨作业缓存。多个archive file以逗号分隔。任务可以通过该参数来使用archive file,支持文件夹嵌套和自动解压。
在下面的例子中
临时缓存文件会被分发到各个计算节点的hadoop.tmp.dir(core-site.xml中配置)路径下
比如data.tar.gz 会在解压后设定一个软连接data -> data/rel/所以使用时 要用 data/data/fileName
-archives hdfs://host:fs_port/data.tar.gz#data \
-mapper "python map.py data/data/ip_area.txt data/data/area_detail.txt" \
2.1.4 其他
# 选填;指定一个app配置文件
-conf configuration_file
# 选填;指定一个namenode
-fs host:port or local Optional Specify a namenode
# 选填;指定需要被拷贝到MR任务节点classpath的jar文件,多个以逗号分隔。
-libjars Optional Specify comma-separated jar files to include in the classpath
2.2 Streaming Command Options 流式命令选项
2.2.1 Mapper
# 必填;指定mapper运行命令
-mapper "python map_step1.py" \
2.2.2 Reducer
# 必填; 指定reducer运行命令,这里 area_detail.txt和$TOP_N为参数传递到reduce_step1.py
-reducer "python reduce_step1.py area_detail.txt $TOP_N" \
2.2.3 输入输出
# 定义的一些INPUT文件夹HDFS路径
INPUT_PATH1="/user/chengc/test/output/ad_bi/20180729"
INPUT_PATH2="/user/chengc/test/output/ad_bi/20180730"
INPUT_PATH3="/user/chengc/test/output/ad_bi/20180731"
# MR任务输出HDFS路径
OUTPUT_PATH_STEP1="/user/chengc/test/output/ad_bi/pv/step1"
# 必填;指定map任务数据输入路径
-input $INPUT_PATH1,$INPUT_PATH2,$INPUT_PATH3 \
# 必填;指定MR任务完成后结果数据输出路径
-output $OUTPUT_PATH_STEP1 \
2.2.4 文件上传
TOP_N=$1
# 选填;-file 指定依赖的本地文件,会上传到hdfs临时目录
-file ./map_step1.py \
-file ./reduce_step1.py \
-file /Users/chengc/cc/study/小象大数据实战/python/Python基础代码/python_basic_project/ad_bi/etl/area_detail.txt
2.2.5 作业插件
跟通常MR作业相同,可以指定以下插件:
# 选填;默认是TextInputFormat。用来指定读取、切分Text为<key,value>对儿的Java类
-inputformat JavaClassName
# 选填;默认是TextOutputformat。
-outputformat JavaClassName
# 选填;指定key的分区方式。
-partitioner JavaClassName
# 选填;map输出中使用的combiner。
-combiner streamingCommand or JavaClassName
0x03 高级参数
3.1 Partitioner-数据切分
前面提到过,一行数据从开始到第一个制表符(\t)前视作key
,其余部分(不包括制表符)作为value
。这一小节讲讲怎么通过配置进行自定义设置。
# 指定mapper从stdout中读出的每行数据切分key的分隔符为 "."
-D stream.map.output.field.separator=. \
# 指定前行头到第四个 "."号间的数据为key,"."以后的数据(不包括".") 作为value
-D stream.num.map.output.key.fields=4 \
# 指定partition时分隔符为"."
-D map.output.key.field.separator=. \
# 指定partition时 使用第一个和第二个field
-D mapreduce.partition.keypartitioner.options=-k1,2
在以上例子中,如果一行数据少于4个"."号,那么整行就会被作为key而value是一个空的Text Object
(相当于new Text("")
)。
而后两个配置可以让key中的前两个field相同的<key,value>
被分区到相同的reducer进行处理。
既然有了以上例子,那么自然就可以想到使用-D stream.reduce.output.field.separator=
和 -D stream.num.reduce.output.fields=NUM
指定reducer输出时的切分以及用stream.map.input.field.separator
和 stream.reduce.input.field.separator
分别为mapper和reducer任务指定输入分隔符(默认为制表符)。
下面说一个将前两个field作为partition key的实例:
首先看看数据示意图:
11.12.1.2
11.14.2.3
11.11.4.1
11.12.1.1
11.14.2.2
好,我们现在看看reducer partition示意图:
11.11.4.1
-----------
11.12.1.2
11.12.1.1
-----------
11.14.2.3
11.14.2.2
然后,请谨记,sort
默认以整个key作为比较的主体,下面是排序示意图:
11.11.4.1
-----------
11.12.1.1
11.12.1.2
-----------
11.14.2.2
11.14.2.3
可以看到,11.12.1.1
排在了11.12.1.2
前面,11.14.2.2
排在了11.14.2.3
前面。
3.2 Comparator-排序比较
Hadoop Streaming中可以自定义排序规则,下面是一个示例:
hadoop jar hadoop-streaming-2.7.6.jar \
# 该类可以支持mr任务输出的key自定义排序
-D mapreduce.job.output.key.comparator.class=org.apache.hadoop.mapreduce.lib.partition.KeyFieldBasedComparator \
-D stream.map.output.field.separator=. \
-D stream.num.map.output.key.fields=4 \
# mr任务输出的key中的field以.分隔
-D mapreduce.map.output.key.field.separator=. \
# MR框架会使用key中的第二个field来排序输出结果
# n表示number(数字),r表示reversed(逆序)
-D mapreduce.partition.keycomparator.options=-k2,2nr \
-D mapreduce.job.reduces=1 \
-input myInputDirs \
-output myOutputDir \
-mapper cat \
-reducer cat
看完了命令,我们看下实例,下面是一个map任务的key输出:
11.12.1.2
11.14.2.3
11.11.4.1
11.12.1.1
11.14.2.2
经过上面的脚本排序后的结果如下:
11.14.2.3
11.14.2.2
11.12.1.2
11.12.1.1
11.11.4.1
可以看到,直接将第二个fileld作为标准进行比较排序。
3.3 Aggregate Package
关于聚合功能,作者暂未使用,有兴趣的请点击这里
3.4 Field Selection Class
这个功能可以指定map/reduce输出key和value的field构成,详情请点击这里
0x04 FAQ
官方文档中列出一些常见问题,请点击这里