Hadoop Streaming with Python(入门)

Hadoop Streaming with Python(新手向)
概述
Hadoop Streaming是Hadoop提供的一种编程工具,允许用户用任何可执行程序和脚本作为mapper和reducer来完成Map/Reduce任务,这意味着你如果只是hadoop的一个轻度使用者,你完全可以用Hadoop Streaming+Python/Ruby/Golang/C艹 等任何你熟悉的语言来完成你的大数据探索需求,又不需要写上很多代码。
hadoop streaming的工作方式
hadoop streaming的工作方式如下图(在这里我们只谈跟hadoop streaming相关的部分,至于MapReduce的细节不予赘述)。与标准的MapReduce(以下简称MR)一样的是整个MR过程依然由mapper、[combiner]、reducer组成(其中combiner为可选加入)。用户像使用java一样去用其他语言编写MR,只不过Mapper/Reducer的输入和输出并不是和java API打交道,而是通过该语言下的标准输入输出函数来进行。我在图中尤其标注了绿色的框框,是你应该关注并自己编写的mapper和reducer的位置。


mapper的角色:hadoop将用户提交的mapper可执行程序或脚本作为一个单独的进程加载起来,这个进程我们称之为mapper进程 ,hadoop不断地将文件片段转换为行,传递到我们的mapper进程中,mapper进程通过标准输入的方式一行一行地获取这些数据,然后设法将其转换为键值对,再通过标准输出的形式将这些键值对按照一对儿一行的方式输出出去。
虽然在我们的mapper函数 中,我们自己能分得清key/value(比方说有可能在我们的代码中使用的是string key,int value),但是当我们采用标准输出之后,key value是打印到一行作为结果输出的(比如sys.stdout.write("%s\t%s\n"%(birthyear,gender))),因此我们为了保证hadoop能从中鉴别出我们的键值对,键值对中一定要以分隔符'\t'即Tab(也可自定义分隔符)字符分隔,这样才能保证hadoop正确地为我们进行partitoner、shuffle等等过程。
reducer的角色:hadoop将用户提交的reducer可执行程序或脚本同样作为一个单独的进程加载起来,这个进程我们称之为reducer进程 ,hadoop不断地将键值对(按键排序)按照一对儿一行的方式传递到reducer进程中,reducer进程同样通过标准输入的方式按行获取这些键值对儿,进行自定义计算后将结果通过标准输出的形式输出出去。
在reducer这个过程中需要注意的是:传递进reducer的键值对是按照键排过序的,这点是由MR框架的sort过程保证的,因此如果读到一个键与前一个键不同,我们就可以知道当前key对应的pairs已经结束了,接下来将是新的key对应的pairs。
Hadoop Streaming的使用方式
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/.../hadoop-streaming.jar [genericOptions] [streamingOptions]
在这行命令中,其实是有先后顺序的,我们一定要保证[genericOptions]写在[streamingOptions]之前,否则hadoop streaming命令将失效。
按照以上的格式使用该命令需要在安装hadoop时设置好环境变量HADOOP_HOME,将其值设置为hadoop的安装目录,当然如果觉得以上的调用方式还是麻烦的话,也可以把hadoop设置进系统的PATH环境变量并用hadoop jar $HADOOP_HOME/.../hadoop-streaming.jar [genericOptions] [streamingOptions]的格式调用。

另外在指定hadoop-streaming.jar时,可能你装的hadoop版本不同,那这个jar的位置也不同,我们需要根据自己的实际情况来确定这个hadoop-streaming.jar的位置,并将其填入命令中,比如我的hadoop-streaming.jar在如下这个目录,/modules/hadoop-2.6.0-cdh5.11.1/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.11.1.jar,而且长的和hadoop-streaming.jar也不太一样,这是因为我是用了cloudera再发行版hadoop安装的结果。这一点对于新同学来讲会有些迷惑,不过你可以在hadoop安装目录里搜索一下,长的是我这个样子的就很可能是可用的jar。
常用的genericOptions如下:
- -D property=value 指定额外的配置信息变量,详情在后文介绍
- -files file1,file2,... 指定需要拷贝到集群节点的文件,格式以逗号分隔,通常为我们自己编写的mapper和reducer脚本或可执行程序,因为你的mapper和reducer通常要由集群中不同的节点来执行,而很可能你的脚本或可执行程序仅仅存在于你提交任务时所用的那个节点,因此遇到这种情况便需要将它们分发出去,-files其后的参数用不用引号括起来都可以。
常用的streamingOptions如下:
- -file filename 指定需要拷贝到集群节点的文件,与-files的功能类似,只不过如果使用-file的话,就需要一个文件一个文件地去上传,比方说如果我要将我的mapper.py,reducer.py 上传到集群上去运行,那就得需要两个-file参数。而在实际使用-file时,hadoop似乎并不希望我们使用-file参数,比如如下这条warning。“18/03/26 20:17:40 WARN streaming.StreamJob: -file option is deprecated, please use generic option -files instead.”
- -input myInputDirs 指定给mapreduce任务输入的文件位置,通常为hdfs上的文件路径,多个文件或目录用逗号隔开
- -output myOutputDir 指定给mapreduce任务输出的目录,通常为hdfs上的文件路径。
- -mapper executable or JavaClassName 用于mapper的可执行程序或java类,如果是脚本文件 ,应该以命令行完整调用的格式作为可执行程序参数并且须加引号,比如-mapper "python mapper.py" \
- -reducer executable or JavaClassName 用于reducer的可执行程序或java类,要求同上
- -partitioner JavaClassName 自定义的partitionerjava类
- -combiner streamingCommand or JavaClassName 自定义的combiner类或命令
常用的-D property=value如下:
- -D mapred.job.name=jobname 指定作业名称
- -D mapred.map.tasks=numofmap 每个Job运行map task的数量
- -D mapred.reduce.tasks=numofreduce 每个Job运行reduce task的数量,如果指定为0,则意味着提交了一个map only的任务
- -D stream.map.input.field.separator 指定map输入时的分隔符,默认为"\t"
- -D stream.map.output.field.separator 指定map输出时使用的key/value分隔符,默认为"\t",比如在我们的mapper中,输出key/value pairs的标准输出语句很可能是这样的 sys.stdout.write("%s,%s\n"%(birthyear,gender)),由于使用了非默认的分隔符,因此需要额外指定分隔符","。
- -D stream.reduce.input.field.separator 指定reduce输入时的分隔符,默认为"\t"
- -D stream.reduce.output.field.separator 指定reduce输入时的分隔符,默认为"\t"
- -D stream.num.map.output.key.fields=num 指定map输出中第几个分隔符作为key和value的分隔点,默认为1
- -D stream.num.reduce.output.fields=num 指定reduce输出中第几个分隔符作为key和value的分隔点,默认为1
- -D stream.non.zero.exit.is.failure=false/true 指定当mapper和reducer未返回0时,hadoop是否该认为此任务执行失败。默认为true。当mapper和reducer的返回值不是0或没有返回值时,hadoop将认为该任务为异常任务,将被再次执行,默认尝试4次都不是0,整个job都将失败。因此,如果我们在编写mapper和reducer未返回0时,则应该将该参数设置为false,否则hadoop streaming任务将报出异常。
示例数据与程序
示例数据
该数据为一部分儿童的信息数据sampleTest.csv,第一个属性为用户id,birthday为用户的生日,gender表示男女,0为女,1为男,2为未知。假设我们的问题是:在这些儿童中,每一年出生的男孩和女孩各是多少。
user_id,birthday,gender
2757,20130311,1
415971,20121111,0
1372572,20120130,1
10339332,20110910,0
10642245,20130213,0
10923201,20110830,1
11768880,20120107,1
12519465,20130705,1
12950574,20090708,0
13735440,20120323,0
14510892,20140812,1
14905422,20110429,1
15786531,20080922,0
16265490,20091209,0
17431245,20110115,0
18190851,20110101,0
20087991,20100808,0
20570454,20081017,1
21137271,20110204,1
21415917,20060801,1
21887268,20100526,0
22602471,20090601,1
23208537,20080416,1
23927133,20081029,0
24829944,20140826,1
52529655,20130611,2
流程解析
我们首先梳理下我们用此数据的MapReduce场景,并思考我们的mapper接收到的数据是什么样的,又应该将处理后的数据输出成什么样的。流程如下图(有缩减)。

mapper的思路是一行一行的获取MR传入的原始数据记录,然后将记录分割成多个字段,获取其中的生日和性别字段,之后将结果打印到标准输出中。需要注意的是该段数据是有title的所以要想办法跳过这段title,我的方法是判断到id则跳过这一行数据。之前我尝试过在代码中采用跳过mapper读到的第一行的方法,但是当MR任务的map task数量设置为2时,结果居然少统计了一个孩子的信息,后来经过朋友的帮助才发现犯了多么低级的错误:当任务设置为两个mapper时,MR将原文件数据分别发送给两个mapper节点 ,此时我们有两个节点在运行我们的mapper程序 ,如果在mapper程序中简单的通过在for循环中continue掉第一轮循环的话,势必导致两个mapper都skip掉一行,那么其中一个mapper将skip掉title,另一个mapper则会skip掉一行数据,在此问题上详述了一番,希望各位在实现自己的MR时不要犯这么低级的错误。
reducer的大概思路是一行一行地获取到按key排过序的key/value对儿(“排序行为”是MR框架为我们做的,不需要我们自己指定),由于MR框架已经为我们排好序,因此只要观察到当前行获得的key与上一行获得的key不一样,即可判断是新的birthyear组,然后累加每一组的男孩和女孩数,遇到新的组时将上一birthyear组的男孩和女孩数目打印出来。
示例程序如下
mapper示例程序
import sys
for data in sys.stdin:
data = data.strip()
record = data.split(’,’)
user_id = record[0]
if user_id == “user_id”:
continue
birthyear = record[1][0:4]
gender = record[2]
sys.stdout.write("%s\t%s\n"%(birthyear,gender))
reducer示例程序
import sys
numByGender = {‘0’:0,‘1’:0,‘2’:0}
lastKey = False
for data in sys.stdin:
data = data.strip()
record = data.split(’\t’)
curKey = record[0]
gender = record[1]
if lastKey and curKey !=lastKey:
sys.stdout.write("%s year:%s female,%s male \n"%(lastKey,numByGender[‘0’],numByGender[‘1’]))
lastKey = curKey
numByGender = {‘0’:0,‘1’:0,‘2’:0}
numByGender[gender] +=1
else:
lastKey = curKey
numByGender[gender] += 1
if lastKey:
sys.stdout.write("%s year:%s female,%s male \n"%(lastKey,numByGender[‘0’],numByGender[‘1’]))
在本机管道下测试MapReduce程序
我们可以使用Linux的管道操作来测试一下编写的Mapper和Reducer。
Linux管道的功能 是可以轻易地连接两个毫不相关的程序,把一个程序的结果交给另一个来处理,甚至,不停地交接处理。想对管道操作了解更多的朋友可以移步csdn,介绍管道的文章很多,在此就不详细介绍了,只说明我们要用的管道操作就好,下面的管道命令含义为:“将sampleTest.csv作为数据传递给mapper.py处理,再将处理后的数据进行排序,之后再把数据交给reducer.py处理。其中有几点需要注意,(1)为了使.py摇身一变成为可执行程序需要python指令才行,(2)sort为排序,-t表示指定分隔符,-t ’ ‘表示用tab进行分隔,-k表示排序时指定的键是在分隔后的哪个field,-k 1表示按分隔符分隔后的第一个field,也即我们在mapper中打印的key/value对儿中的key。
这样我们就可以简单测试一下mapper和reducer的执行情况。
cat /home/sunyunfeng/jars/sampleTest.csv | python /home/sunyunfeng/jars/mapper.py | sort -t ’ ’ -k 1 | python /home/sunyunfeng/jars/reducer.py
结果如下
2006 year:0 female,1 male
2008 year:2 female,2 male
2009 year:2 female,1 male
2010 year:2 female,0 male
2011 year:3 female,3 male
2012 year:2 female,2 male
2013 year:1 female,2 male
2014 year:0 female,2 male
与我们预想的一样。Well done。
在集群上运行MapReduce程序
接着我们把sampleTest.csv上传到hdfs上,并使用hadoop streaming命令来在集群上运行我们的程序。请注意一定要将-D,-files参数放在所有参数前面,我们之前说过genericOptions一定要放在streamingOptions前面,而-files -D都属于genericOptions,所以你懂的,之前曾把-files放在后面,执行起来后各种错误。另外不要忘了使用-D stream.non.zero.exit.is.failure=false 来避免MR未返回0时异常退出,原因正如上文介绍该参数时一样,在此只做提醒不再解释。
#复制这段命令时记得把’>'去掉,那是在linux上运行分行命令时linux自己加上的
[sunyunfeng@master ~]$ hadoop jar /modules/hadoop-2.6.0-cdh5.11.1/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.11.1.jar
> -D stream.non.zero.exit.is.failure=false
> -files /home/sunyunfeng/jars/mapper.py,/home/sunyunfeng/jars/reducer.py
> -input /user/sunyunfeng/sampleTest.csv
> -output /user/sunyunfeng/output
> -mapper “python mapper.py”
> -reducer “python reducer.py”
18/03/28 23:02:04 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable
packageJobJar: [/tmp/hadoop-unjar2006139001052554179/] [] /tmp/streamjob6060134998108625697.jar tmpDir=null
18/03/28 23:02:06 INFO client.RMProxy: Connecting to ResourceManager at master/192.168.74.133:8032
18/03/28 23:02:07 INFO client.RMProxy: Connecting to ResourceManager at master/192.168.74.133:8032
18/03/28 23:02:09 WARN hdfs.DFSClient: Caught exception
java.lang
.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1281)
at java.lang.Thread.join(Thread.java:1355)
at org.apache.hadoop.hdfs
.DFSOutputStream
D
a
t
a
S
t
r
e
a
m
e
r
.
c
l
o
s
e
R
e
s
p
o
n
d
e
r
(
D
F
S
O
u
t
p
u
t
S
t
r
e
a
m
.
j
a
v
a
:
951
)
a
t
o
r
g
.
a
p
a
c
h
e
.
h
a
d
o
o
p
.
h
d
f
s
.
D
F
S
O
u
t
p
u
t
S
t
r
e
a
m
DataStreamer.closeResponder(DFSOutputStream.java:951) at org.apache.hadoop.hdfs.DFSOutputStream
DataStreamer.closeResponder(DFSOutputStream.java:951)atorg.apache.hadoop.hdfs.DFSOutputStreamDataStreamer.endBlock(DFSOutputStream.java:689)
at org.apache.hadoop.hdfs.DFSOutputStream
D
a
t
a
S
t
r
e
a
m
e
r
.
r
u
n
(
D
F
S
O
u
t
p
u
t
S
t
r
e
a
m
.
j
a
v
a
:
878
)
18
/
03
/
2823
:
02
:
09
I
N
F
O
m
a
p
r
e
d
.
F
i
l
e
I
n
p
u
t
F
o
r
m
a
t
:
T
o
t
a
l
i
n
p
u
t
p
a
t
h
s
t
o
p
r
o
c
e
s
s
:
118
/
03
/
2823
:
02
:
10
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
S
u
b
m
i
t
t
e
r
:
n
u
m
b
e
r
o
f
s
p
l
i
t
s
:
218
/
03
/
2823
:
02
:
11
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
S
u
b
m
i
t
t
e
r
:
S
u
b
m
i
t
t
i
n
g
t
o
k
e
n
s
f
o
r
j
o
b
:
j
o
b
1
52137406379
1
0
06218
/
03
/
2823
:
02
:
11
I
N
F
O
i
m
p
l
.
Y
a
r
n
C
l
i
e
n
t
I
m
p
l
:
S
u
b
m
i
t
t
e
d
a
p
p
l
i
c
a
t
i
o
n
a
p
p
l
i
c
a
t
i
o
n
1
52137406379
1
0
06218
/
03
/
2823
:
02
:
11
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
T
h
e
u
r
l
t
o
t
r
a
c
k
t
h
e
j
o
b
:
h
t
t
p
:
/
/
m
a
s
t
e
r
:
8088
/
p
r
o
x
y
/
a
p
p
l
i
c
a
t
i
o
n
1
52137406379
1
0
062
/
18
/
03
/
2823
:
02
:
11
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
R
u
n
n
i
n
g
j
o
b
:
j
o
b
1
52137406379
1
0
06218
/
03
/
2823
:
02
:
33
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
J
o
b
j
o
b
1
52137406379
1
0
062
r
u
n
n
i
n
g
i
n
u
b
e
r
m
o
d
e
:
f
a
l
s
e
18
/
03
/
2823
:
02
:
33
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
m
a
p
018
/
03
/
2823
:
02
:
53
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
m
a
p
5018
/
03
/
2823
:
03
:
13
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
m
a
p
10018
/
03
/
2823
:
03
:
15
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
m
a
p
10018
/
03
/
2823
:
03
:
19
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
m
a
p
10018
/
03
/
2823
:
03
:
34
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
J
o
b
j
o
b
1
52137406379
1
0
062
c
o
m
p
l
e
t
e
d
s
u
c
c
e
s
s
f
u
l
l
y
18
/
03
/
2823
:
03
:
34
I
N
F
O
m
a
p
r
e
d
u
c
e
.
J
o
b
:
C
o
u
n
t
e
r
s
:
53
F
i
l
e
S
y
s
t
e
m
C
o
u
n
t
e
r
s
F
I
L
E
:
N
u
m
b
e
r
o
f
b
y
t
e
s
r
e
a
d
=
231
F
I
L
E
:
N
u
m
b
e
r
o
f
b
y
t
e
s
w
r
i
t
t
e
n
=
376781
F
I
L
E
:
N
u
m
b
e
r
o
f
r
e
a
d
o
p
e
r
a
t
i
o
n
s
=
0
F
I
L
E
:
N
u
m
b
e
r
o
f
l
a
r
g
e
r
e
a
d
o
p
e
r
a
t
i
o
n
s
=
0
F
I
L
E
:
N
u
m
b
e
r
o
f
w
r
i
t
e
o
p
e
r
a
t
i
o
n
s
=
0
H
D
F
S
:
N
u
m
b
e
r
o
f
b
y
t
e
s
r
e
a
d
=
1014
H
D
F
S
:
N
u
m
b
e
r
o
f
b
y
t
e
s
w
r
i
t
t
e
n
=
224
H
D
F
S
:
N
u
m
b
e
r
o
f
r
e
a
d
o
p
e
r
a
t
i
o
n
s
=
9
H
D
F
S
:
N
u
m
b
e
r
o
f
l
a
r
g
e
r
e
a
d
o
p
e
r
a
t
i
o
n
s
=
0
H
D
F
S
:
N
u
m
b
e
r
o
f
w
r
i
t
e
o
p
e
r
a
t
i
o
n
s
=
2
J
o
b
C
o
u
n
t
e
r
s
L
a
u
n
c
h
e
d
m
a
p
t
a
s
k
s
=
3
L
a
u
n
c
h
e
d
r
e
d
u
c
e
t
a
s
k
s
=
1
D
a
t
a
−
l
o
c
a
l
m
a
p
t
a
s
k
s
=
3
T
o
t
a
l
<
s
p
a
n
>
t
i
m
e
<
/
s
p
a
n
>
s
p
e
n
t
b
y
a
l
l
m
a
p
s
i
n
o
c
c
u
p
i
e
d
s
l
o
t
s
(
m
s
)
=
22756
T
o
t
a
l
t
i
m
e
s
p
e
n
t
b
y
a
l
l
r
e
d
u
c
e
s
i
n
o
c
c
u
p
i
e
d
s
l
o
t
s
(
m
s
)
=
21735
T
o
t
a
l
t
i
m
e
s
p
e
n
t
b
y
a
l
l
m
a
p
t
a
s
k
s
(
m
s
)
=
22756
T
o
t
a
l
t
i
m
e
s
p
e
n
t
b
y
a
l
l
r
e
d
u
c
e
t
a
s
k
s
(
m
s
)
=
21735
T
o
t
a
l
v
c
o
r
e
−
m
i
l
l
i
s
e
c
o
n
d
s
t
a
k
e
n
b
y
a
l
l
m
a
p
t
a
s
k
s
=
22756
T
o
t
a
l
v
c
o
r
e
−
m
i
l
l
i
s
e
c
o
n
d
s
t
a
k
e
n
b
y
a
l
l
r
e
d
u
c
e
t
a
s
k
s
=
21735
T
o
t
a
l
m
e
g
a
b
y
t
e
−
m
i
l
l
i
s
e
c
o
n
d
s
t
a
k
e
n
b
y
a
l
l
m
a
p
t
a
s
k
s
=
23302144
T
o
t
a
l
m
e
g
a
b
y
t
e
−
m
i
l
l
i
s
e
c
o
n
d
s
t
a
k
e
n
b
y
a
l
l
r
e
d
u
c
e
t
a
s
k
s
=
22256640
M
a
p
−
R
e
d
u
c
e
F
r
a
m
e
w
o
r
k
M
a
p
i
n
p
u
t
r
e
c
o
r
d
s
=
26
M
a
p
o
u
t
p
u
t
r
e
c
o
r
d
s
=
25
M
a
p
o
u
t
p
u
t
b
y
t
e
s
=
175
M
a
p
o
u
t
p
u
t
m
a
t
e
r
i
a
l
i
z
e
d
b
y
t
e
s
=
237
I
n
p
u
t
s
p
l
i
t
b
y
t
e
s
=
202
C
o
m
b
i
n
e
i
n
p
u
t
r
e
c
o
r
d
s
=
0
C
o
m
b
i
n
e
o
u
t
p
u
t
r
e
c
o
r
d
s
=
0
R
e
d
u
c
e
i
n
p
u
t
g
r
o
u
p
s
=
8
R
e
d
u
c
e
s
h
u
f
f
l
e
b
y
t
e
s
=
237
R
e
d
u
c
e
i
n
p
u
t
r
e
c
o
r
d
s
=
25
R
e
d
u
c
e
o
u
t
p
u
t
r
e
c
o
r
d
s
=
8
S
p
i
l
l
e
d
R
e
c
o
r
d
s
=
50
S
h
u
f
f
l
e
d
M
a
p
s
=
2
F
a
i
l
e
d
S
h
u
f
f
l
e
s
=
0
M
e
r
g
e
d
M
a
p
o
u
t
p
u
t
s
=
2
G
C
t
i
m
e
e
l
a
p
s
e
d
(
m
s
)
=
358
C
P
U
t
i
m
e
s
p
e
n
t
(
m
s
)
=
3360
P
h
y
s
i
c
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
s
n
a
p
s
h
o
t
=
518832128
V
i
r
t
u
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
s
n
a
p
s
h
o
t
=
4528226304
T
o
t
a
l
c
o
m
m
i
t
t
e
d
h
e
a
p
u
s
a
g
e
(
b
y
t
e
s
)
=
257544192
P
e
a
k
M
a
p
P
h
y
s
i
c
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
=
206704640
P
e
a
k
M
a
p
V
i
r
t
u
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
=
1508040704
P
e
a
k
R
e
d
u
c
e
P
h
y
s
i
c
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
=
108732416
P
e
a
k
R
e
d
u
c
e
V
i
r
t
u
a
l
m
e
m
o
r
y
(
b
y
t
e
s
)
=
1635336192
S
h
u
f
f
l
e
E
r
r
o
r
s
B
A
D
I
D
=
0
C
O
N
N
E
C
T
I
O
N
=
0
I
O
E
R
R
O
R
=
0
W
R
O
N
G
L
E
N
G
T
H
=
0
W
R
O
N
G
M
A
P
=
0
W
R
O
N
G
R
E
D
U
C
E
=
0
F
i
l
e
I
n
p
u
t
F
o
r
m
a
t
C
o
u
n
t
e
r
s
B
y
t
e
s
R
e
a
d
=
812
F
i
l
e
O
u
t
p
u
t
F
o
r
m
a
t
C
o
u
n
t
e
r
s
B
y
t
e
s
W
r
i
t
t
e
n
=
22418
/
03
/
2823
:
03
:
34
I
N
F
O
s
t
r
e
a
m
i
n
g
.
S
t
r
e
a
m
J
o
b
:
O
u
t
p
u
t
d
i
r
e
c
t
o
r
y
:
/
u
s
e
r
/
s
u
n
y
u
n
f
e
n
g
/
o
u
t
p
u
t
[
s
u
n
y
u
n
f
e
n
g
@
m
a
s
t
e
r
]
DataStreamer.run(DFSOutputStream.java:878) 18/03/28 23:02:09 INFO mapred.FileInputFormat: Total input paths to process : 1 18/03/28 23:02:10 INFO mapreduce.JobSubmitter: number of splits:2 18/03/28 23:02:11 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1521374063791_0062 18/03/28 23:02:11 INFO impl.YarnClientImpl: Submitted application application_1521374063791_0062 18/03/28 23:02:11 INFO mapreduce.Job: The url to track the job: http://master:8088/proxy/application_1521374063791_0062/ 18/03/28 23:02:11 INFO mapreduce.Job: Running job: job_1521374063791_0062 18/03/28 23:02:33 INFO mapreduce.Job: Job job_1521374063791_0062 running in uber mode : false 18/03/28 23:02:33 INFO mapreduce.Job: map 0% reduce 0% 18/03/28 23:02:53 INFO mapreduce.Job: map 50% reduce 0% 18/03/28 23:03:13 INFO mapreduce.Job: map 100% reduce 0% 18/03/28 23:03:15 INFO mapreduce.Job: map 100% reduce 67% 18/03/28 23:03:19 INFO mapreduce.Job: map 100% reduce 100% 18/03/28 23:03:34 INFO mapreduce.Job: Job job_1521374063791_0062 completed successfully 18/03/28 23:03:34 INFO mapreduce.Job: Counters: 53 File System Counters FILE: Number of bytes read=231 FILE: Number of bytes written=376781 FILE: Number of read operations=0 FILE: Number of large read operations=0 FILE: Number of write operations=0 HDFS: Number of bytes read=1014 HDFS: Number of bytes written=224 HDFS: Number of read operations=9 HDFS: Number of large read operations=0 HDFS: Number of write operations=2 Job Counters Launched map tasks=3 Launched reduce tasks=1 Data-local map tasks=3 Total <span>time</span> spent by all maps in occupied slots (ms)=22756 Total time spent by all reduces in occupied slots (ms)=21735 Total time spent by all map tasks (ms)=22756 Total time spent by all reduce tasks (ms)=21735 Total vcore-milliseconds taken by all map tasks=22756 Total vcore-milliseconds taken by all reduce tasks=21735 Total megabyte-milliseconds taken by all map tasks=23302144 Total megabyte-milliseconds taken by all reduce tasks=22256640 Map-Reduce Framework Map input records=26 Map output records=25 Map output bytes=175 Map output materialized bytes=237 Input split bytes=202 Combine input records=0 Combine output records=0 Reduce input groups=8 Reduce shuffle bytes=237 Reduce input records=25 Reduce output records=8 Spilled Records=50 Shuffled Maps =2 Failed Shuffles=0 Merged Map outputs=2 GC time elapsed (ms)=358 CPU time spent (ms)=3360 Physical memory (bytes) snapshot=518832128 Virtual memory (bytes) snapshot=4528226304 Total committed heap usage (bytes)=257544192 Peak Map Physical memory (bytes)=206704640 Peak Map Virtual memory (bytes)=1508040704 Peak Reduce Physical memory (bytes)=108732416 Peak Reduce Virtual memory (bytes)=1635336192 Shuffle Errors BAD_ID=0 CONNECTION=0 IO_ERROR=0 WRONG_LENGTH=0 WRONG_MAP=0 WRONG_REDUCE=0 File Input Format Counters Bytes Read=812 File Output Format Counters Bytes Written=224 18/03/28 23:03:34 INFO streaming.StreamJob: Output directory: /user/sunyunfeng/output [sunyunfeng@master ~]
DataStreamer.run(DFSOutputStream.java:878)18/03/2823:02:09INFOmapred.FileInputFormat:Totalinputpathstoprocess:118/03/2823:02:10INFOmapreduce.JobSubmitter:numberofsplits:218/03/2823:02:11INFOmapreduce.JobSubmitter:Submittingtokensforjob:job1521374063791006218/03/2823:02:11INFOimpl.YarnClientImpl:Submittedapplicationapplication1521374063791006218/03/2823:02:11INFOmapreduce.Job:Theurltotrackthejob:http://master:8088/proxy/application15213740637910062/18/03/2823:02:11INFOmapreduce.Job:Runningjob:job1521374063791006218/03/2823:02:33INFOmapreduce.Job:Jobjob15213740637910062runninginubermode:false18/03/2823:02:33INFOmapreduce.Job:map018/03/2823:02:53INFOmapreduce.Job:map5018/03/2823:03:13INFOmapreduce.Job:map10018/03/2823:03:15INFOmapreduce.Job:map10018/03/2823:03:19INFOmapreduce.Job:map10018/03/2823:03:34INFOmapreduce.Job:Jobjob15213740637910062completedsuccessfully18/03/2823:03:34INFOmapreduce.Job:Counters:53FileSystemCountersFILE:Numberofbytesread=231FILE:Numberofbyteswritten=376781FILE:Numberofreadoperations=0FILE:Numberoflargereadoperations=0FILE:Numberofwriteoperations=0HDFS:Numberofbytesread=1014HDFS:Numberofbyteswritten=224HDFS:Numberofreadoperations=9HDFS:Numberoflargereadoperations=0HDFS:Numberofwriteoperations=2JobCountersLaunchedmaptasks=3Launchedreducetasks=1Data−localmaptasks=3Total<span>time</span>spentbyallmapsinoccupiedslots(ms)=22756Totaltimespentbyallreducesinoccupiedslots(ms)=21735Totaltimespentbyallmaptasks(ms)=22756Totaltimespentbyallreducetasks(ms)=21735Totalvcore−millisecondstakenbyallmaptasks=22756Totalvcore−millisecondstakenbyallreducetasks=21735Totalmegabyte−millisecondstakenbyallmaptasks=23302144Totalmegabyte−millisecondstakenbyallreducetasks=22256640Map−ReduceFrameworkMapinputrecords=26Mapoutputrecords=25Mapoutputbytes=175Mapoutputmaterializedbytes=237Inputsplitbytes=202Combineinputrecords=0Combineoutputrecords=0Reduceinputgroups=8Reduceshufflebytes=237Reduceinputrecords=25Reduceoutputrecords=8SpilledRecords=50ShuffledMaps=2FailedShuffles=0MergedMapoutputs=2GCtimeelapsed(ms)=358CPUtimespent(ms)=3360Physicalmemory(bytes)snapshot=518832128Virtualmemory(bytes)snapshot=4528226304Totalcommittedheapusage(bytes)=257544192PeakMapPhysicalmemory(bytes)=206704640PeakMapVirtualmemory(bytes)=1508040704PeakReducePhysicalmemory(bytes)=108732416PeakReduceVirtualmemory(bytes)=1635336192ShuffleErrorsBADID=0CONNECTION=0IOERROR=0WRONGLENGTH=0WRONGMAP=0WRONGREDUCE=0FileInputFormatCountersBytesRead=812FileOutputFormatCountersBytesWritten=22418/03/2823:03:34INFOstreaming.StreamJob:Outputdirectory:/user/sunyunfeng/output[sunyunfeng@master ] hdfs dfs -cat /user/sunyunfeng/output/part-00000
18/03/28 23:09:17 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable
2006 year:0 female,1 male
2008 year:2 female,2 male
2009 year:2 female,1 male
2010 year:2 female,0 male
2011 year:3 female,3 male
2012 year:2 female,2 male
2013 year:1 female,2 male
2014 year:0 female,2 male
可以看到在集群上运行的结果和预期一致,并与管道测试结果一致。当然也有可能出现不一致的情况,如果是这样我们该关注的点应该放到分布式架构上面而不是代码的计算逻辑上,因为在集群上运行和本机测试,主要的不同是:在集群上是多个mapper和reducer共同运行,而在本机你可以理解为一个mapper和reducer,而上文我们谈到的“为了skip掉title而额外skip掉一行数据”的错误就产生于此。
再来说说mapreduce任务的结果,刚开始看到这么多结果信息一定很头疼,但是看熟了感觉起来也就是那么回事。建议各位关注以下几个信息,如果最后的结果与我们的预期不一样,也可以从这些信息里面获取一些线索。
Map input records=26 #mapper输入的记录数
Map output records=25 #mapper输出的记录数
Map output bytes=175 #mapper输出的数据的字节数
Reduce input records=25 #reducer输入的记录数
Reduce output records=8 #reducer输出的记录数
File Input Format Counters
Bytes Read=812 #任务输入的字节数
File Output Format Counters
Bytes Written=224 #任务输出的字节数
其他
在参考其他书籍与文章的时候发现对于支持Hadoop Streaming的语言环境搭建的描述少之又少,而按照某些资料尝试却又很难得出正确结果,这会导致很多新手在尝试Hadoop Streaming时磕磕绊绊,甚至无法进展。因此我将在这里叙述一些你在使用Hadoop Streaming时可能遇到的、非常基础的、但是就是可能会困扰你很久的小问题。对于成功使用Hadoop Streaming的用户可以直接忽略掉以下内容。
Hadoop Streaming与python环境
hadoop是基于集群的,因此我们的MR任务是运行于集群中的各个节点上的,正如我们使用集群时需要为集群中的节点安装java环境一样,如果你想用python来实现MapReduce,当然也需要为各个节点配置好python环境。
那么问题就来了,我应该为节点配置什么样的环境,我如何让我的hadoop命令或者python脚本与该环境一致。这里将涉及三个方面
- 你需要保证你的python安装正确并且能使用python命令行的方式启动python
拿下图这条命令为例,-files参数表示将mapper.py和reducer.py发送到集群中的各个节点去运行,-mapper "python mapper.py"表示发送到这些节点的.py文件是使用"python xxx.py"的命令行方式启动成进程的。因此你必须保证这些节点拥有这样的能力。检验的方法是在你的节点的Terminal下面输入python -V,如果能正确显示python版本那表示安装正确,否则你可能需要花点时间把python重新装好了。
[sunyunfeng@master bin]$ python -V
Python 3.5.0
[sunyunfeng@master bin]$ ^C
hadoop jar /modules/hadoop-2.6.0-cdh5.11.1/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.11.1.jar
-D stream.non.zero.exit.is.failure=false
-files /home/sunyunfeng/jars/mapper.py,/home/sunyunfeng/jars/reducer.py
-input /user/sunyunfeng/sampleTest.csv
-output /user/sunyunfeng/output
-mapper “python mapper.py”
-reducer “python reducer.py"
- 你需要保证你知道你的python命令启动的是你的节点中的哪个版本
这个保证看起来很搞笑,我自己的机器我还不知道我安装的是什么版本吗,可是这种问题我就遇到了,我的CentOS是自带python2.7的,可是我就是想用python3.5 怎么办,我可以安装python3.5,这没有问题,但是很抱歉,在Terminal下面输入python -V时,出来的却是python 2.7.5,其实这并不是你的python没装好,而是你的配置不对,这时候你应该查看你的usr/bin 下面的python软连接情况,如果像我这样,那说明你的python软连接是连接在python2上的,而python2又软连接至python2.7上了,所以输入python -V才出现python2.7.5。
Last login: Thu Mar 29 21:32:58 2018 from 192.168.74.1
[sunyunfeng@slave1 ~]$ python -V
Python 2.7.5
[sunyunfeng@slave1 ~]$ ls -l /usr/bin |grep python
-rwxr-xr-x. 1 root root 11216 Dec 1 2015 abrt-action-analyze-python
lrwxrwxrwx. 1 root root 7 Mar 29 21:31 python -> python2
lrwxrwxrwx. 1 root root 9 Jan 17 15:34 python2 -> python2.7
-rwxr-xr-x. 1 root root 7136 Nov 20 2015 python2.7
lrwxrwxrwx. 1 root root 33 Mar 17 17:18 python3 -> /modules/Python-3.5.0/bin/python3
[sunyunfeng@slave1 ~]$
你如果一定要使用python3.5作为你的MR,要么你在/usr/bin下面建立python3到你的python安装目录下bin/python3的软连接,并把-mapper “python mapper.py” \这一行改成-mapper “python3 mapper.py” 来使用,要么你删除python到python2或python2.7的软连接,重新建立python到你的python安装目录下bin/python3的软连接。我选择的是后者。由于我之前安装python3.5的时候已经在/usr/bin建立了python3到python安装目录中bin/python3的软连接,因此我直接在/usr/bin中删除原有python软连接并建立python到python3的软连接就可以了。
[sunyunfeng@slave1 bin]$ cd /usr/bin/
[sunyunfeng@slave1 bin]$ sudo rm python
[sunyunfeng@slave1 bin]$ sudo ln -s python3 python
[sunyunfeng@slave1 bin]$ ls -l |grep python
-rwxr-xr-x. 1 root root 11216 Dec 1 2015 abrt-action-analyze-python
lrwxrwxrwx. 1 root root 7 Mar 29 22:09 python -> python3
lrwxrwxrwx. 1 root root 9 Jan 17 15:34 python2 -> python2.7
-rwxr-xr-x. 1 root root 7136 Nov 20 2015 python2.7
lrwxrwxrwx. 1 root root 33 Mar 17 17:18 python3 -> /modules/Python-3.5.0/bin/python3
[sunyunfeng@slave1 bin]$ python -V
Python 3.5.0
经过这么一番检查和折腾我想你应该知道你的python命令运行的是哪个版本了
- 你需要保证你的代码语法适应所有节点安装的python版本
这个很重要,因为你是写了一个mapper.py和一个reducer.py,但是却要把他们扔到集群上去运行。如果你用的是python3的语法(python3与python2的语法略有不同,我们要用的标准输入输出就如此),扔到一个“部分是python2环境”、“部分是python3环境”的集群,那么那些python2环境的节点一定运行不了你的程序。举例来讲,python3的标准输出长这样 sys.stdout.write(”%s\t%s\n"%(birthyear,gender)),而python2的长这样 print “%s\t%s” %(birthyear,gender),如果你的集群上装的是python2的环境,你使用python3的语法,那一定run不起来,还会报一堆你看不懂的、觉得八竿子打不着的错误。
打包python环境到集群
有一个办法可以解决为各个节点配置环境的问题,那就是将环境打包到集群,并在执行命令时分发到各个节点供其使用。为此你应该首先将你的python安装目录压缩,然后上传到hdfs,之后在你的hadoop streaming命令中就可以把这个hdfs上的环境分发到集群上去运行了。
[sunyunfeng@master modules]$ ls
hadoop-2.6.0-cdh5.11.1 jdk1.7.0_79 Python-3.5.0
#压缩Python-3.5.0文件夹(安装目录)至python35.tar.gz
[sunyunfeng@master modules]$ tar czf python35.tar.gz Python-3.5.0
#检查压缩情况
[sunyunfeng@master modules]$ ls
hadoop-2.6.0-cdh5.11.1 jdk1.7.0_79 Python-3.5.0 python35.tar.gz
#在hdfs上新建一个目录
[sunyunfeng@master modules]$ hdfs dfs -mkdir -p /env
18/03/29 22:59:27 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable
#将压缩包上传到集群
[sunyunfeng@master modules]$ hdfs dfs -put python35.tar.gz /env
18/03/29 23:00:03 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable
#确认已上传到集群
[sunyunfeng@master modules]$ hdfs dfs -ls /env
18/03/29 23:05:47 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable
Found 1 items
-rw-r–r-- 3 sunyunfeng supergroup 96659428 2018-03-29 23:03 /env/python35.tar.gz
[sunyunfeng@master modules]$
你可以像这样使用hadoop streaming命令,来使hdfs上的环境分发到集群去运行。
hadoop jar /modules/hadoop-2.6.0-cdh5.11.1/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.11.1.jar
-D stream.non.zero.exit.is.failure=false
-files /home/sunyunfeng/jars/mapper.py,/home/sunyunfeng/jars/reducer.py
-archives “hdfs://master/env/python35.tar.gz#py”
-input /user/sunyunfeng/sampleTest.csv
-output /user/sunyunfeng/output
-mapper “py/Python-3.5.0/bin/python3.5 mapper.py”
-reducer "py/Python-3.5.0/bin/python3.5 reducer.py"
需要注意的是-archives中的master,要更换成你的hdfs的对应名称,我的namenode的主机名是master。另外"hdfs://master/env/python35.tar.gz#py"中的#py表示将hdfs上的这个文件分发到集群中各个节点之后再解压到名为py的文件夹中,因此你需要在-mapper中使用解压后文件夹中的python程序来启动你的脚本。说的详细些,python35.tar.gz压缩前是叫Python-3.5.0的,而且它是个目录,Python-3.5.0/bin中原来是有python3.5的,现在Python-3.5.0目录被我们压缩成python35.tar.gz,然后你把python35.tar.gz上传到了hdfs,现在你把hdfs上的python35.tar.gz分发到集群中,命令其解压到py文件夹中,那么自然需要从py/Python-3.5.0/bin中找到你的python3.5程序。
python脚本的规范与陷阱
python是个很神奇的语言,它不使用诸如C艹、java的大括号{ }这个破烂玩意儿,而使用缩进来表示语句块,因此你很可能遇到过用notepad++写完python脚本却用不了的情况,这估计是你将空格和Tab混着用作缩进了。比如下图这种情况的代码,如果你传到集群上去使用一定是用不了的,会报错TabError: Inconsistent use of tabs and spaces in indentation。所以你应该注意代码的规范,不要混用空格和Tab来完成缩进。空格和Tab可以通过notepad++的视图->显示符号->显示所有字符查看。我的第13行用了两个Tab,其他行用的空格。

另外也请尊重Linux使用的UTF-8字符集,所以你如果妄图在Windows上使用记事本之类的写代码还带中文注释的,传到集群上若报各种错误的,种种种种的,记得用notepad++转成UTF-8格式。
推荐阅读
-
Spark:安装及环境配置指南!
前篇文章介绍了scala的安装与配置、接下来介绍一下spark的安装及环境配置。 1、Apache spark下载 在浏览器输入网址 https://spark.apache.org/downloads.html进入spark的下载页面,如下图所…
Spark 快速教程及在线体验
python 实现hadoop的mapreduce
为了用python实现mapreduce,我们先引入下面两个个知识 sys.stdin()itertools之groupbysys模块的简单学习sys.stdin 是一个文件描述符,代表标准输入,不需使用open函数打开,就可以使用 例…
Mac OS X 上搭建 Hadoop 开发环境指南
22 条评论
我就喜欢这么详细的
C艹?
多谢作者写这么详尽容易入门的文章
nice啊,很详细
Hi,『由于MR框架已经为我们排好序,因此只要观察到当前行获得的key与上一行获得的key不一样,即可判断是新的birthyear组』 ,MR框架中,保证了每个reducer处理的输入,包含了每个key的全部list吗? 会不会产生 reducer 1 处理了k1的一部分,reducer2处理了k1的剩下一部分呢?
很详细,感谢!
感谢分享
感谢分享!
好文章!很详细~
特意登录了来评论,这个文章真的太棒了
请教一下 如果mapper或reducer是一个工程里面包括多个文件和子目录怎么在集群环境下提交任务呢?多谢指导!
用通配符 *