Hadoop Streaming 编程

标签: Hadoop | 发表时间:2012-05-30 02:29 | 作者:jrckkyy
出处:http://hi.baidu.com/jrckkyy

1、概述

Hadoop Streaming是Hadoop提供的一个编程工具,它允许用户使用任何可执行文件或者脚本文件作为Mapper和Reducer,例如:

采用shell脚本语言中的一些命令作为mapper和reducer(cat作为mapper,wc作为reducer)

$HADOOP_HOME/bin/hadoop  jar $HADOOP_HOME/hadoop-streaming.jar \

-input myInputDirs \

-output myOutputDir \

-mapper cat \

-reducer wc

本文安排如下,第二节介绍Hadoop Streaming的原理,第三节介绍Hadoop Streaming的使用方法,第四节介绍Hadoop Streaming的程序编写方法,在这一节中,用C++、C、shell脚本 和python实现了WordCount作业,第五节总结了常见的问题。 文章最后给出了程序下载地址。(本文内容基于Hadoop-0.20.2版本)

(注:如果你采用的语言为C或者C++,也可以使用Hadoop Pipes,具体可参考这篇文章: Hadoop Pipes编程。)

关于Hadoop Streaming高级编程方法,可参考这篇文章: Hadoop Streaming高级编程

2、Hadoop Streaming原理

mapper和reducer会从标准输入中读取用户数据,一行一行处理后发送给标准输出。Streaming工具会创建MapReduce作业,发送给各个tasktracker,同时监控整个作业的执行过程。

如果一个文件(可执行或者脚本)作为mapper,mapper初始化时,每一个mapper任务会把该文件作为一个单独进程启动,mapper任务运行时,它把输入切分成行并把每一行提供给可执行文件进程的标准输入。 同时,mapper收集可执行文件进程标准输出的内容,并把收到的每一行内容转化成key/value对,作为mapper的输出。 默认情况下,一行中第一个tab之前的部分作为 key,之后的(不包括tab)作为 value 如果没有tab,整行作为key值,value值为null。

对于reducer,类似。

以上是Map/Reduce框架和streaming mapper/reducer之间的基本通信协议。

3、Hadoop Streaming用法

Usage: $HADOOP_HOME/bin/hadoop jar \

$HADOOP_HOME/hadoop-streaming.jar [options]

options:

(1)-input:输入文件路径

(2)-output:输出文件路径

(3)-mapper:用户自己写的mapper程序,可以是可执行文件或者脚本

(4)-reducer:用户自己写的reducer程序,可以是可执行文件或者脚本

(5)-file:打包文件到提交的作业中,可以是mapper或者reducer要用的输入文件,如配置文件,字典等。

(6)-partitioner:用户自定义的partitioner程序

(7)-combiner:用户自定义的combiner程序(必须用java实现)

(8)-D:作业的一些属性(以前用的是-jonconf),具体有:

             1)mapred.map.tasks:map task数目
             2)mapred.reduce.tasks:reduce task数目
             3)stream.map.input.field.separator/stream.map.output.field.separator: map task输入/输出数
据的分隔符,默认均为\t。
             4)stream.num.map.output.key.fields:指定map task输出记录中key所占的域数目
             5)stream.reduce.input.field.separator/stream.reduce.output.field.separator:reduce task输入/输出数据的分隔符,默认均为\t。
             6)stream.num.reduce.output.key.fields:指定reduce task输出记录中key所占的域数目
另外,Hadoop本身还自带一些好用的Mapper和Reducer:
(1)    Hadoop聚集功能

Aggregate提供一个特殊的reducer类和一个特殊的combiner类,并且有一系列的“聚合器”(例如“sum”,“max”,“min”等)用于聚合一组value的序列。用户可以使用Aggregate定义一个mapper插件类,这个类用于为mapper输入的每个key/value对产生“可聚合项”。Combiner/reducer利用适当的聚合器聚合这些可聚合项。要使用Aggregate,只需指定“-reducer aggregate”。

(2)字段的选取(类似于Unix中的‘cut’)

Hadoop的工具类org.apache.hadoop.mapred.lib.FieldSelectionMapReduc帮助用户高效处理文本数据,就像unix中的“cut”工具。工具类中的map函数把输入的key/value对看作字段的列表。 用户可以指定字段的分隔符(默认是tab),可以选择字段列表中任意一段(由列表中一个或多个字段组成)作为map输出的key或者value。 同样,工具类中的reduce函数也把输入的key/value对看作字段的列表,用户可以选取任意一段作为reduce输出的key或value。

4、 Mapper和Reducer实现

本节试图用尽可能多的语言编写Mapper和Reducer,包括Java,C,C++,Shell脚本,python等。

由于Hadoop会自动解析数据文件到Mapper或者Reducer的标准输入中,以供它们读取使用,所有应先了解各个语言获取标准输入的方法。

(1)    Java语言:

见Hadoop自带例子

(2)    C++语言

1
2
3
4
string key;
while(cin>>key){
cin>>value;
 ….

(3)  C语言

1
2
3
4
5
charbuffer[BUF_SIZE];
while(fgets(buffer, BUF_SIZE - 1, stdin)){
intlen = strlen(buffer);
}

(4)  Shell脚本

用管道

(5)  Python脚本

1
2
3
import sys
for line in sys.stdin:
.......

为了说明各种语言编写Hadoop Streaming程序的方法,下面以WordCount为例,WordCount作业的主要功能是对用户输入的数据中所有字符串进行计数。

(1) C语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//mapper
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
  
#define BUF_SIZE        2048
#define DELIM   "\n"
  
intmain(intargc, char*argv[]){
     charbuffer[BUF_SIZE];
     while(fgets(buffer, BUF_SIZE - 1, stdin)){
            intlen = strlen(buffer);
            if(buffer[len-1] == '\n')
             buffer[len-1] = 0;
  
            char*querys  = index(buffer, ' ');
            char*query = NULL;
            if(querys == NULL) continue;
            querys += 1; /*  not to include '\t' */
  
            query = strtok(buffer, " ");
            while(query){
                   printf("%s\t1\n", query);
                   query = strtok(NULL, " ");
            }
     }
     return0;
}
//---------------------------------------------------------------------------------------
//reducer
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
  
#define BUFFER_SIZE     1024
#define DELIM   "\t"
  
intmain(intargc, char*argv[]){
 charstrLastKey[BUFFER_SIZE];
 charstrLine[BUFFER_SIZE];
 intcount = 0;
  
 *strLastKey = '\0';
 *strLine = '\0';
  
 while( fgets(strLine, BUFFER_SIZE - 1, stdin) ){
 char*strCurrKey = NULL;
 char*strCurrNum = NULL;
  
 strCurrKey  = strtok(strLine, DELIM);
 strCurrNum = strtok(NULL, DELIM); /* necessary to check error but.... */
  
 if( strLastKey[0] == '\0'){
 strcpy(strLastKey, strCurrKey);
 }
  
 if(strcmp(strCurrKey, strLastKey)){
 printf("%s\t%d\n", strLastKey, count);
 count = atoi(strCurrNum);
 }else{
 count += atoi(strCurrNum);
 }
 strcpy(strLastKey, strCurrKey);
  
 }
 printf("%s\t%d\n", strLastKey, count); /* flush the count */
 return0;
}

(2) C++语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//mapper
#include <stdio.h>
#include <string>
#include <iostream>
usingnamespacestd;
  
intmain(){
        string key;
        string value = "1";
        while(cin>>key){
                cout<<key<<"\t"<<value<<endl;
        }
        return0;
}
//------------------------------------------------------------------------------------------------------------
//reducer
#include <string>
#include <map>
#include <iostream>
#include <iterator>
usingnamespacestd;
intmain(){
        string key;
        string value;
        map<string, int> word2count;
        map<string, int>::iterator it;
        while(cin>>key){
                cin>>value;
                it = word2count.find(key);
                if(it != word2count.end()){
                        (it->second)++;
                }
                else{
                        word2count.insert(make_pair(key, 1));
                }
        }
  
        for(it = word2count.begin(); it != word2count.end(); ++it){
                cout<<it->first<<"\t"<<it->second<<endl;
        }
        return0;
}

(3) shell脚本语言实现

1
2
3
4
5
$HADOOP_HOME/bin/hadoop  jar $HADOOP_HOME/hadoop-streaming.jar \
    -input myInputDirs \
    -output myOutputDir \
    -mapper cat\
   -reducer  wc

(4) Python脚本语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python
  
importsys
  
# maps words to their counts
word2count ={}
  
# input comes from STDIN (standard input)
forline insys.stdin:
    # remove leading and trailing whitespace
    line =line.strip()
    # split the line into words while removing any empty strings
    words =filter(lambdaword: word, line.split())
    # increase counters
    forword inwords:
        # write the results to STDOUT (standard output);
        # what we output here will be the input for the
        # Reduce step, i.e. the input for reducer.py
        #
        # tab-delimited; the trivial word count is 1
        print'%s\t%s'%(word, 1)
#---------------------------------------------------------------------------------------------------------
#!/usr/bin/env python
  
fromoperator importitemgetter
importsys
  
# maps words to their counts
word2count ={}
  
# input comes from STDIN
forline insys.stdin:
    # remove leading and trailing whitespace
    line =line.strip()
  
    # parse the input we got from mapper.py
    word, count =line.split()
    # convert count (currently a string) to int
    try:
        count =int(count)
        word2count[word] =word2count.get(word, 0) +count
    exceptValueError:
        # count was not a number, so silently
        # ignore/discard this line
        pass
  
# sort the words lexigraphically;
#
# this step is NOT required, we just do it so that our
# final output will look more like the official Hadoop
# word count examples
sorted_word2count =sorted(word2count.items(), key=itemgetter(0))
  
# write the results to STDOUT (standard output)
forword, count insorted_word2count:
    print'%s\t%s'%(word, count)

5、常见问题

(1)作业总是运行失败:

需要把mapper文件和reducer文件放到各个tasktracker上,保证各个节点均有一份。也可在提交作业时,采用-file选项指定这些文件。

(2)用脚本编写时,第一行需注明脚本解释器,默认是shell

6、参考资料

【1】 C++&Python实现Hadoop Streaming的paritioner和模块化

【2】 如何在Hadoop中使用Streaming编写MapReduce

【3】 Hadoop如何与C++结合

【4】 Hadoop Streaming和pipes理解

7、程序打包下载

文章中用到的程序源代码可在 此处下载

原创文章,转载请注明: 转载自 董的博客

本文链接地址: http://dongxicheng.org/mapreduce/hadoop-streaming-programming/


阅读全文
类别: Hadoop  查看评论

相关 [hadoop streaming 编程] 推荐:

Hadoop Streaming 编程

- - 学着站在巨人的肩膀上
Hadoop Streaming是Hadoop提供的一个编程工具,它允许用户使用任何可执行文件或者脚本文件作为Mapper和Reducer,例如:. 采用shell脚本语言中的一些命令作为mapper和reducer(cat作为mapper,wc作为reducer). 本文安排如下,第二节介绍Hadoop Streaming的原理,第三节介绍Hadoop Streaming的使用方法,第四节介绍Hadoop Streaming的程序编写方法,在这一节中,用C++、C、shell脚本 和python实现了WordCount作业,第五节总结了常见的问题.

Hadoop MapReduce高级编程

- - 互联网 - ITeye博客
•combine函数把一个map函数产生的对(多个key, value)合并成一个新的. 将新的作为输入到reduce函数中,其格式与reduce函数相同. •这样可以有效的较少中间结果,减少网络传输负荷. •什么情况下可以使用Combiner.

Hadoop MapReduce编程入门案例

- - CSDN博客云计算推荐文章
Hadoop入门例程简析中. (下面的程序下载地址: http://download.csdn.net/detail/zpcandzhj/7810829). (1)Hadoop新旧API的区别. 新的API倾向于使用虚类(抽象类),而不是接口,因为这更容易扩展. 例如,可以无需修改类的实现而在虚类中添加一个方法(即用默认的实现).

hadoop编程:分析CSDN注册邮箱分布情况

- - CSDN博客云计算推荐文章
hadoop编程:分析CSDN注册邮箱分布情况. 本文博客链接: http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 主机:Ubuntu10.04. hadoop版本:1.2.1. 开发工具:eclipse4.4.0. 要求:原始数据共6428632条,分析不同邮箱的注册情况,并按使用人数从大到小排序.

[原]Spark Streaming原理简析

- - 张包峰的博客
StreamingContext实例化的时候,需要传入一个 SparkContext,然后指定要连接的 spark matser url,即连接一个 spark engine,用于获得executor. 实例化之后,首先,要指定一个接收数据的方式,如. 这样从socket接收文本数据. 这个步骤返回的是一个 ReceiverInputDStream的实现,内含 Receiver,可接收数据并转化为RDD放内存里.

Spark Streaming 调优实践

- - IT瘾-dev
分享嘉宾:肖力涛 拼多多 资深算法工程师. 注:欢迎转载,转载请注明出处. 在使用 Spark 和 Spark Streaming 时,当我们将应用部署在集群上时,可能会碰到运行慢、占用过多资源、不稳定等问题,这时需要做一些优化才能达到最好的性能. 有时候一个简单的优化可以起到化腐朽为神奇的作用,使得程序能够更加有效率,也更加节省资源.

Nginx+ffmpeg搭建Apple Http Live Streaming笔记

- - 移动开发 - ITeye博客
起始Nginx来搭建HLS步骤非常少. 安装好Nginx,然后跑起来. 把切片好的视频和m3u8文件放到部署目录,直接访问就可以了. 网上国内国外的找了好多博客. 这里一定注意一点,不要用VLC播放器来测试,最好用iPad或者iPhone,再么用Safari 开发模式下模拟iPad、iPhone的浏览器模式播放.

Spark Streaming 自定义接收器

- - zzm
Spark Streaming可以从任意数据源接受流数据,而不仅仅是那些内置支持的数据源(如Flume、kafka等). 这就要求开发人员实现一个接收器(recevier),用于接收来自有关数据源的数据. 本篇手册以一个自定义的接收器(recevier)实现和其在spark streaming中的应为为主线进行讲解.

Spark Streaming 与 Kafka 整合的改进 | SmartSi

- -
Apache Kafka 正在迅速成为最受欢迎的开源流处理平台之一. 我们在 Spark Streaming 中也看到了同样的趋势. 因此,在 Apache Spark 1.3 中,我们专注于对 Spark Streaming 与 Kafka 集成进行重大改进. 为 Kafka 新增了 Direct API - 这允许每个 Kafka 记录在发生故障时只处理一次,并且不使用  Write Ahead Logs.

GitHub - allwefantasy/streamingpro: Build Spark Batch/Streaming/MLlib Application by SQL

- -
StreamingPro 中文文档. 应用模式:写json配置文件,StreamingPro启动后执行该文件,可以作为批处理或者流式程序. 服务模式:启动一个StreamingPro Server作为常驻程序,然后通过http接口发送MLSQL脚本进行交互. 我们强烈推荐使用第二种模式,第一种模式现在已经不太更新了,现在迅速迭代的是第二种模式,并且第二种模式可以构建AI平台.