hadoop streaming详解

1,233 阅读6分钟

1/Streaming的作用

<1>Hadoop Streaming框架,最大的好处是:
     可以让任何语言编写的map,reduce程序在hadoop集群上运行
     
<2>map/reduce程序只要遵循从标准输入stdin读取数据,写出到标准输出stdout即可

<3>其次,容易进行单机调试,通过管道符前后相接的方式就可以模拟streaming,
   在本地完成map/reduce程序的调试
         cat inputfile | mapper | sort | reducer > output
         
<4>最后,streaming框架还提供了作业job提交时的丰富参数控制,
   直接通过streaming参数,而不需要使用java语言修改;
   很多mapreduce的高阶功能,都可以通过steaming参数的设置和调整来完成。

2/Streaming的局限

<1>Streaming默认只能处理文本数据Textfile,
   对于二进制数据,比较好的方法是将二进制的key, value进行base64编码,转化为文本

<2>Mapper和reducer的前后都要进行标准输入和标准输出的转化,涉及数据拷贝和解析,带来了一定的开销

3/Streaming命令的形式

hadoop jar hadoop_streaming.jar [普通选项]  [Streaming选项]      
注意:普通选项一定要写在[streaming选项]前面

4/普通选项有哪些

参考网址: https://www.cnblogs.com/shay-zhangjin/p/7714868.html

 -D 普通选项,使用最多的高级参数,替代-jobconf(参数将被废弃),需要注意的是 -D选项要放在streaming参数的前面
 -D mapred.job.name="Test001"   # 指定hadoop作业名称
 
 -D mapred.map.tasks=2     # 指定map的个数,不一定生效
 -D mapred.reduce.tasks=2  # 指定reduce的个数,一定生效
 -D mapred.reduce.tasks=0   # 如果hadoop作业只有mapper阶段, mapper的输出直接作为作业的输出


 -D stream.map.output.field.separator=.   # 分隔符 
 -D stream.num.map.output.key.fields=4    # 前几列作为key,剩下的作为value
 指定Mapper输出的key,value分隔符
 Mapper的输出使用.做分割符,并且第4个.之前的部分作为key, 剩余的部分作为value (包含剩余的.)
 如果mapper的输出没有4个., 则整体一行作为key, value为空
 默认情况下:使用\t做分隔符,第1个\t之前的部分作为key, 剩余为value, 如果mapper输出没有\t,则整体一行作为key,value为空


 -D stream.reduce.output.field.seperator=. 
 -D stream.num.reduce.output.key.fields=4
 指定reduce输出根据.分割,第4个.之前的内容为key,其他为value
 Reducer程序要根据指定进行key,value的构造,不像map那样有默认情况

 -D mapred.job.priority=HIGH   
 # hadoop作业的优先级,有VERY_LOW, LOW, NORMAL, HIGH, VERY_HIGH几种优先级可以设置。

 -D mapred.job.map.capacity=5     # 最多同时运行的map任务
 -D mapred.job.reduce.capacity=3  # 最多同时运行的reduce任务

 -D mapred.task.timeout=6000  # Task没有响应(输入输出)的最大时间,超时后,该task被终止

 指定列数,partition分桶
     -D num.key.fields.for.partition=1       # 只用1列Key做分桶
     -D num.key.fields.for.partition=2       # 使用1,2共两列key做分桶

 指定某些字段做key
     -D mapred.text.key.partitioner.option =-k1,2   # 第1,2列Key做分桶
     -D mapred.text.key.partitioner.option =-k2,2   # 第2列key做分桶

 -D mapreduce.reduce.memory.mb=512  #单位为M,指定每个reduce task申请的内存数量

5/streaming选项

 -input   
  # 支持*通配符,指定多个文件或目录,多次-input,指定多个输入文件/目录。Mapper的输入数据,文件要在任务提交前手动上传到HDFS
  
 -output 
 # 路径不能已存在,否则认为是其他job的输出.reducer输出结果的HDFS存放路径,  不能已存在,但脚本中一定要配置
 
 -mapper  
 # Mapper程序,例如 -mapper “python mapper.py”
 
 -reducer 
 # Reducer程序,不需要reduce处理就不指定
 
 -file 
 #本地mapper、reducer程序文件、程序运行需要的其他文件,文件在本地,将本地文件分发给计算节点

6/Mapper输入/输出,根据哪些key分桶,根据哪些key进行排序

Mapper输入:
             每一个mapper开始运行时,输入文件会被转换成多行(TextInputformat根据\n来进行分行),并将每一行传递给stdin, 作为Mapper的输入, mapper直接对stdin中的每行内容做处理
             
     Mapper输出分隔符:
             默认情况下hadoop设置mapper输出的key, value通过tab进行分隔,可以重新指定
             -D stream.map.output.field.seperator=.    # 指定mapper每条输出key,value分隔符
             -D stream.num.map.output.key.fields=4     # 第4个.之前的部分为key,剩余为value
             
     Mapper的输出会经历:
             1、 partition前,根据mapper输出分隔符分离出Key和Value;
                  -D stream.map.output.field.separator=.      # 指定mapper每条输出key,value分隔符
                  -D stream.num.map.output.key.fields=4       # 第4个.之前的为key, 剩下的为value
                  -D  map.output.key.field.separator=.        # 设置map输出中,Key内部的分隔符
             2、 根据 “分桶分隔符”,确定哪些key被用来做partition(默认是用所有key, 只有1列; 或者是Mapper输出分隔符分离出的所有key都被用于Partition)
                 基于指定的Key进行分桶,打标签
                 指定列数
                 -D num.key.fields.for.partition=1        # 只用1列Key做分桶,也就是第一列
                 -D num.key.fields.for.partition=2        # 使用1,2共两列key做分桶(列数)
                 
                 
     Reducer的输入:
             每个Reducer的每条输入,就是去除Partition标签(根据Mapper分隔符分离出partition标签)后的内容,和Mapper输出到stdout中的内容相同,但不同记录之间已经做了排序;因此如果重新指定了Mapper的输出分隔符,Reducer程序就要修改为根据新的Mapper输出分隔符来分离Key,value;
             
     Reducer的输出:
             Reducer的输出,默认也是根据tab来分离key,value, 这也是reducer程序要根据tab来组合key,value输出给stdout的原因;  当Reducer输出分隔符重新指定,Reducer程序中输出给stdout的内容也要配合新的分隔符来构造(Reducer->stdout-> outputformat ->file,  outputformat根据reducer的输出分隔符来分离key,value,  并写入文件)
            -D stream.reduce.output.field.seperator=.      # reducer输出key,value间的分隔符
            -D stream.num.reduce.output.key.fields=4       # 第4个.之前的内容为key, 其他为value
            

7/hadoop集群执行python脚本

     首先在本地把文件上传到hdfs分布式文件存储系统
             hdfs dfs -put test.txt /user_hadoop/houzhen03/input
     在集群上执行hadoop任务:
             hadoop jar hadoop_streaming.jar -input 输入文件 -output 输出文件存放的目录 -mapper "python mapper.py" -reducer 'python reducer.py' -file /home/hadoop/mapper.py -file /home/hadoop/rreducer.py
             -file一定是绝对路径
     作业完成之后,查看输出结果:
             hdfs dfs -cat 输出文件存放的目录/part-00000

8/ hadoop脚本

     import os
        
     hadoop_path = "/home/hadoop/hadoop-2.9.2/bin/hadoop jar "   # hadoop路径,注意空格
     hadoop_streaming_path = " /home/hadoop/hadoop-2.9.2/share/hadoop/tools/lib/hadoop-streaming-2.9.2.jar "
     input_path = " /user_hadoop/houzhen03/input/1.txt "  # 输入路径,可以是一个文件,也可以是含有很多文件的目录
     或者 input_path = " /user_hadoop/houzhen03/input "  # 一个目录
     output_path = " /user_hadoop/houzhen03/output/999 "  # 规定输出路径,必须是之前不存在的,如果已经存在,必须先删除
        
     def main(input_path,output_path):
         command = hadoop_path + hadoop_streaming_path + \
                   " -input " + input_path + \
                   " -output " + output_path + \
                   " -mapper \'python mapper.py\'" + \     # 执行mapper.py程序,必须写python
                   " -reducer \'python reducer.py\'" + \   # 执行reduer.py程序,必须写python
                   " -file /home/hadoop/mapper.py" + \     # 绝对路径,需要先在本地,chmod 777 mapper.py
                   " -file /home/hadoop/reducer.py"        # 绝对路径,需要先在本地,chmod 777 reducer.py
        
     # 执行hadoop指令
     #print("执行命令为:\n",command)
     os.system(command)
        
     main(input_path,output_path)