OS X下MapReduce程序运行的几种模式

1.MapReduce程序运行的模式简介

  1. 程序运行模式

    • 本地模式
      • 利用本地的JVM运行,使用本地的IDE进行debug
    • 远程模式
      • 提交至远程的集群上运行,使用本地的IDE进行debug
      • 提交至远程的集群上运行,不使用本地IDE进行debug
  2. 数据存放路径

    • 远程文件系统(hdfs)
    • 本地文件系统(local file system)


2.开发环境简介

  • 操作系统:macOS Sierra 10.12.6
  • Java版本:1.8.0_131-b11
  • Hadoop版本:hadoop-2.7.4
  • IDE:IntelliJ IDEA


3.MapReduce程序运行例子

3.1 程序需求

学校里开设了多门课程,有语文(chinese)、数学(math)、英语(english)等。经过了一次年级统考后,每个学生的成绩都被记录在多个文本文件中,文本文件格式如下。

  • math.txt
Ben 75
Jack 60
May 85
Tom 91
  • english.txt
Jack 72
May 60
Tom 62
Ben 90
  • chinese.txt
Ben 79
May 88
Tom 68
Jack 70

现需要根据以上的文本文件,算出每个学生在本次统考中的平均分,并将结果用一个总的文件averageScore.txt进行存储。averageScore.txt的格式如下。

  • averageScore.txt
#name #score
Ben 0.0
May 0.0
Tom 0.0
Jack 0.0


3.2 程序设计思路

3.2.1 Mapper的处理逻辑

Mapper每次从文本文件中读取1行内容,即调用1次map方法。Mapper需要把原始数据中一行的内容拆分成学生姓名(student name)和该门课程的分数(score)。按照需求,本程序最终要算出每一个学生的平均分,所以学生姓名应作为一个key,对应的value即为该生的平均分(实际上是不严谨的,因为在实际环境中会出现多个学生重名的现象,若不作特殊处理,key是不允许重复的。最根本的解决方案是采用学号作为key,但为了演示直观,仅采用学生姓名作为key)

Mapper读完一行的数据后,把{student name,score}这个key-value写入中间结果,准备传送给Reducer作下一步的运算。

3.2.2 Reducer的处理逻辑

Reducer接收到的数据,实际上是一个key与该key对应的value的一个集合(并不仅仅是一个value)。在本需求中,传入reduce方法的参数是学生姓名,以及该生多门课程分数的集合,类似于Ben,[60,70,80,...]。所以Reducer需要将集合中的分数求和,然后求出平均值,最终得到一个{student name, average score}key-value对。

3.2.3 具体代码设计

  • AVGMapper类
    用于实现map方法
package mr;

import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;

/**
 * Created by marco on 2017/8/17.
 */
public class AVGMapper extends Mapper<LongWritable, Text, Text, DoubleWritable>
{
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException
    {
        String line = value.toString();
        if(line.length() == 0)  // 文件格式错误,出现空行
            return;
        String[] split = line.split(" ");
        String stuName = split[0];
        String stuScore = split[1];
        double score = Double.parseDouble(stuScore);    // 转成double类型,方便后续求均值计算
        context.write(new Text(stuName), new DoubleWritable(score));
    }
}
  • AVGReducer类
    用于实现reduce方法
package mr;

import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

/**
 * Created by marco on 2017/8/17.
 */
public class AVGReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable>
{
    @Override
    protected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException
    {
        double sum = 0;
        int length = 0;
        for(DoubleWritable value : values)
        {
            sum += value.get();
            length++;
        }

        double avgScore = sum / (double)length;
        context.write(key, new DoubleWritable(avgScore));
    }
}

  • AVGRunner类
    用于关联Mapper与Reducer,并创建MapReduce任务(Job)提交运行。基本代码如下所示。
package mr;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
 * Created by marco on 2017/8/17.
 */
public class AVGRunner
{
    static public void main(String[] args) throws Exception
    {
        // 设置hdfs的handler
        Configuration fsConf = new Configuration();
        fsConf.set("fs.default.name","hdfs://localhost:9000/");
        FileSystem fs = FileSystem.get(fsConf);

        // MapReduce的配置参数
        Configuration mrConf = new Configuration();

        // 新建一个求平均值的Job
        Job avgJob = Job.getInstance(mrConf);
        avgJob.setJarByClass(AVGRunner.class);

        // 设置Mapper类与Reducer类
        avgJob.setMapperClass(AVGMapper.class);
        avgJob.setReducerClass(AVGReducer.class);

        // 设置输入输出的数据结构
        avgJob.setMapOutputKeyClass(Text.class);
        avgJob.setMapOutputValueClass(DoubleWritable.class);
        avgJob.setOutputKeyClass(Text.class);
        avgJob.setOutputValueClass(DoubleWritable.class);

        // 检查结果输出目录,若已存在则删除输出目录
        if(fs.exists(new Path("/avg/output")))
        {
            fs.delete(new Path("/avg/output"), true);
        }

        // 设置数据目录以及结果输出目录
        FileInputFormat.setInputPaths(avgJob, new Path(""));
        FileOutputFormat.setOutputPath(avgJob, new Path(""));

        // 提交任务,等待完成
        System.exit(avgJob.waitForCompletion(true)?0:1);
    }
}


3.3 MapReduce程序运行

若使用本地文件系统的数据文件,且在本地模式运行,无需配置hdfs相关的参数,数据目录以及结果输出目录填写本地路径即可。(确保结果输出文件夹未被创建,否则会报异常)

// 均填写本地文件路径即可
FileInputFormat.setInputPaths(avgJob, new Path(""));
FileOutputFormat.setOutputPath(avgJob, new Path(""));


若使用hdfs上的数据文件,且在本地模式运行,应配置hdfs相关参数,数据目录以及结果输出目录均填写hdfs的路径。(确保结果输出文件夹未被创建,否则会报异常)

// 设置hdfs参数,并用该配置创建一个新的Job
Configuration fsConf = new Configuration();
fsConf.set("fs.default.name","hdfs://localhost:9000/");
Job avgJob = Job.getInstance(fsConf);


// 均填写hdfs路径即可
FileInputFormat.setInputPaths(avgJob, new Path(""));
FileOutputFormat.setOutputPath(avgJob, new Path(""));

3.3.1 本地模式运行

本地模式运行,直接编译执行AVGRunner的main方法即可,程序运行结束后会在自行设置的结果输出目录中生成运行结果。

3.3.2 远程集群运行

首先使用IDE将程序打成一个jar包,本例中命名为hadoop.jar

提交到远程集群上运行分两种情况

  • 使用本地IDE(IntelliJ IDEA)运行,任务被提交到集群运行,但可使用IDE进行跟踪debug

    新建一个MapReduce的配置对象,将已经打包好的jar包传入配置中

      // MapReduce的配置参数,远程运行,本地debug
      Configuration mrConf = new Configuration();
      mrConf.set("mapreduce.job.jar","hadoop.jar");
      mrConf.set("mapreduce.framework.name","yarn");
      
      //利用以上配置新建一个Job
      Job avgJob = Job.getInstance(mrConf);
      avgJob.setJarByClass(AVGRunner.class);
    


  • 在终端直接使用hadoop命令将任务提交到集群运行,无法使用IDE进行跟踪debug

    直接在终端中输入hadoop命令

    hadoop jar $jar包名称 $待执行的类的名称
    

    在本例中应输入

    hadoop jar avg.jar mr.AVGRunner
    

####################### 注意⚠️ #######################

在OS X中,使用IntelliJ IDEA打包jar包后,若在终端中直接使用hadoop jar $jar包名称 $待执行的类的名称提交MapReduce任务,会报出异常,因为OS X系统的文件系统对大小写不敏感(case-insensitive)

经过对此异常的搜索,暂时的解决方案是通过删除jar包中的LICENSE文件,使任务顺利提交。

# 在终端中执行以下命令
zip -d $jar包名称 META-INF/LICENSE
zip -d $jar包名称 LICENSE

#####################################################

可以看到使用了hadoop命令提交任务后,系统调用了RPC框架和Yarn框架中的一些服务,用于远程运行,而非使用LocalJobSubmitter于本地运行。

并且在MapReduce任务管理页面可看到任务已经完成的历史记录。

4.总结

MapReduce任务可在本地运行,也可提交到集群上运行。

在开发初期,需要编写Demo程序时,可在本地进行开发与测试,将数据文件放置在本地文件系统,直接使用IDE运行主类的main方法,观察运行结果。

上线前调试,可采用远程模式运行,不直接使用hadoop命令提交,而是使用IDE进行提交与debug,这样既可以保证程序运行在远处集群上(生产环境or开发环境),也可以在本地方便跟踪调试。

可上线时,使用hadoop命令直接提交到远程集群,并通过localhost:50070(默认配置)的任务管理页面进行观察。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容