mapreduce配置及编程实践(eclipse)

前言

本实验在Hadoop基础上完成,请确保可以正确启动Hadoop服务。

基本任务1:环境配置

修改配置文件(文件路径为 /opt/jxxy/hadoop-2.6.5/etc/hadoop )。找到mapred-site.xml文件和yarn-site.xml文件,添加如图所示代码:

<property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
</property>

<property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
</property>

图1 修改mapred-site.xml文件

图2 修改yarn-site.xml文件

基本任务2:编程WordCount

(1)首先创建一个新文件,命令行输入:for i in `seq 100000`;do echo "hello jxxy$i" >> test1.txt;done。

for i in `seq 100000`;do echo "hello jxxy$i" >> test1.txt;done

图3 创建一个新文件test1.txt

新建一个eclipse项目,导入修改配置后的mapred-site.xml,yarn-site.xml文件,导入hadoop_jars包,同时也导入配置Hadoop时修改的hdfs-site.xml,core-site.xml文件。

这里首先需要使用Xftp将需要导入的文件传输到主机。

图4 传输文件

右键src目录,点击import,选择Genneral目录下的File System,之后选择之前传输文件的目录,选择所需文件即可。

图5 import操作

图6 导入文件

(2)编程WordCount主类,MyMapper类,MyReducer类,制作jar包

首先创建WordCount主类。代码如下:

package com.jxxy.mr.test;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;

public class WordCount {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "myjob");
        job.setJar("/root/wordcount.jar");

        // 设置输入/输出路径
        Path inPath = new Path("/user/root/test1.txt");
        Path outPath = new Path("/output/wordcount");
        
        FileInputFormat.addInputPath(job, inPath);
        if (outPath.getFileSystem(conf).exists(outPath)) {
            outPath.getFileSystem(conf).delete(outPath, true); // 删除已存在的输出目录
        }
        FileOutputFormat.setOutputPath(job, outPath);

        // 设置 Mapper 和 Reducer 类
        job.setMapperClass(MyMapper.class);
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

创建MyMapper类,代码如下:

package com.jxxy.mr.test;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.StringTokenizer;

public class MyMapper extends Mapper<Object, Text, Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object key, Text value, Context context) 
            throws IOException, InterruptedException {
        StringTokenizer tokens = new StringTokenizer(value.toString());
        while (tokens.hasMoreTokens()) {
            word.set(tokens.nextToken());
            context.write(word, one); // 输出 <单词, 1>
        }
    }
}

创建MyReducer类,代码如下:

package com.jxxy.mr.test;

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

public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, Context context) 
            throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable val : values) {
            sum += val.get(); // 累加相同单词的计数
        }
        result.set(sum);
        context.write(key, result); // 输出 <单词, 总数>
    }
}

可以先尝试运行程序方便后面制作jar包。这里注意启动Hadoop,通过jps命令查看进程。

图7 启动Hadoop

部署,制作jar包。右键项目,选择Export-Java-Runnable JAR file,导出为可运行的jar包,Launch Configuration选择运行的项目,Export destination选择一个目录,Library handling选择第二个更好。然后点击完成。

这里导出项目之前注意自己在虚拟机配置的Java版本,如果是Java7,则右键项目,点击properties,点击Java compiler,将Compiler compliance level选择1.7,如果是Java8,则选择默认的1.8,无需更改。否则运行程序时将报错。

图8 Java编译器选择

图9 制作jar包

先上传输入文件(也就是前面一开始创建的文件)到hdfs,这里可以选择一开始创建文件的时候顺便上传到目录。然后将jar包上传到集群(放在/root),可以选择使用Xftp将导出的jar包传输到虚拟机root/目录下。

图10 上传文件

图11 上传集群/root

(3)运行程序,统计test.txt文件hello和jxxy出现的次数。然后在浏览器查看,有_SUCCESS文件生成则说明编程成功(浏览器打开:主机名:50070)。

#运行
hadoop jar wordcount.jar com.jxxy.mr.test.WordCount
#查看输出目录
hadoop fs -ls /output/wordcount
#查看文件内容
hadoop fs -cat /output/wordcount/part-r-00000
#也可以选择输出少量输出
hadoop fs -cat /output/wordcount/part-r-00000 | head -n 10

图12 运行结果

图13 浏览器查看信息

进阶任务1:编程实现文件合并和去重操作

任务要求:对于两个输入文件,即文件A和文件B,编写程序对两个文件进行合并,并剔除其中重复的内容,得到一个新的输出文件C。

(1)首先创建测试文件A.txt和B.txt,然后上传到HDFS。

echo -e "apple\nbanana\norange\napple" > A.txt
echo -e "orange\ngrape\napple" > B.txt
hadoop fs -put A.txt B.txt /user/root/

图13 创建测试文件

图14 浏览器查看信息

(2)创建一个新的项目,编程MergeDriver主类,MergeMapper类,MergeReducer类,制作jar包。

创建MergeDriver主类,代码如下:

package MergeDriver;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
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;

import MergeDriver.MergeMapper;
import MergeDriver.MergeReducer;

public class MergeDriver {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "MergeAndDeduplicate");
        job.setJar("/root/merge.jar");

        job.setJarByClass(MergeDriver.class);
        job.setMapperClass(MergeMapper.class);
        job.setReducerClass(MergeReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
        job.setNumReduceTasks(1); // 确保只有一个输出文件

        // 输入文件
        FileInputFormat.addInputPath(job, new Path("/user/root/A.txt"));
        FileInputFormat.addInputPath(job, new Path("/user/root/B.txt"));

        // 输出目录
        Path outputDir = new Path("/output");
        if (outputDir.getFileSystem(conf).exists(outputDir)) {
            outputDir.getFileSystem(conf).delete(outputDir, true);
        }
        FileOutputFormat.setOutputPath(job, outputDir);

        // 提交作业
        boolean success = job.waitForCompletion(true);

        // 作业成功后重命名结果文件
        if (success) {
            FileSystem fs = FileSystem.get(conf);
            Path partFile = new Path(outputDir, "part-r-00000");
            Path finalOutput = new Path(outputDir, "C.txt"); // 目标文件路径
            
            if (fs.exists(partFile)) {
                fs.rename(partFile, finalOutput); // 重命名文件
                fs.delete(new Path(outputDir, "_SUCCESS"), true); // 删除_SUCCESS标记文件
                System.out.println("结果已保存到 /output/C.txt");
            }
        }
        System.exit(success ? 0 : 1);
    }
}

创建MergeMapper类,代码如下:

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

public class MergeMapper extends Mapper<Object, Text, Text, Text> {
    private Text line = new Text();

    public void map(Object key, Text value, Context context) 
            throws IOException, InterruptedException {
        line = value;
        context.write(line, new Text("")); // Value 为空
    }
}

创建MergeReducer类,代码如下:

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

public class MergeReducer extends Reducer<Text, Text, Text, Text> {
    public void reduce(Text key, Iterable<Text> values, Context context) 
            throws IOException, InterruptedException {
        context.write(key, new Text("")); // 去重:相同 Key 只输出一次
    }
}

部署,制作jar包,先上传输入文件到hdfs,然后上传到集群(放在/root)。这里记得修改Java编译版本。

图15 制作jar包

图16 上传集群/root

(3)运行程序。命令行输入hadoop jar merge.jar MergeDriver运行,命令行输入hadoop fs -ls /output和hadoop fs -cat /output/C.txt验证结果。

hadoop jar merge.jar MergeDriver
hadoop fs -ls /output和hadoop fs -cat /output/C.txt

图17 验证结果

图18 浏览器查看信息

这里需要注意的是我在主类中删除了_SUCCESS文件,所以这里并不显示,同时我将目录以及生成的内容文件整合成了C.txt文件,从而符合任务要求。大家也可以在生成文件之后修改。

进阶任务2:编程实现对输入文件的排序

任务要求:现在有多个输入文件,每个文件中的每行内容均为一个整数。要求读取所有文件中的整数,进行升序排序后,输出到一个新的文件中,输出的整数格式为每行两个整数,第一个整数位第二个整数的排序位次,第二个整数位原待排列的整数。(具体样例参见教材P153)

(1)首先创建测试文件num1.txt和num2.txt,然后上传到HDFS。

# 创建测试文件
echo -e "30\n50\n10" > num1.txt
echo -e "10\n20\n40" > num2.txt
# 上传到HDFS
hadoop fs -put num1.txt num2.txt /user/root/

图19 创建测试文件

图20 浏览器查看信息

(2)创建一个新的项目,编程SortDriver主类,SortMapper类,SortReducer类,制作jar包。

创建SortDrive主类,代码如下:

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

public class SortDriver {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Integer Sort");
        job.setJar("/root/sort.jar");

        job.setJarByClass(SortDriver.class);
        job.setMapperClass(SortMapper.class);
        job.setReducerClass(SortReducer.class);

        // 设置输出类型
        job.setOutputKeyClass(IntWritable.class);
        job.setOutputValueClass(IntWritable.class);

        // 输入路径
        FileInputFormat.addInputPath(job, new Path("/user/root/num1.txt"));
        FileInputFormat.addInputPath(job, new Path("/user/root/num2.txt"));
        
        // 输出路径
        Path outputPath = new Path("/output/sort");
        if (outputPath.getFileSystem(conf).exists(outputPath)) {
            outputPath.getFileSystem(conf).delete(outputPath, true);
        }
        FileOutputFormat.setOutputPath(job, outputPath);

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

创建SortMapper类,代码如下:

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

public class SortMapper extends Mapper<LongWritable, Text, IntWritable, IntWritable> {
    private IntWritable number = new IntWritable();

    @Override
    protected void map(LongWritable key, Text value, Context context) 
            throws IOException, InterruptedException {
        try {
            int num = Integer.parseInt(value.toString().trim());
            number.set(num);
            context.write(number, new IntWritable(1)); // 输出格式: <数字, 1>
        } catch (NumberFormatException e) {
            System.err.println("忽略非整数行: " + value);
        }
    }
}

创建SortReducer类,代码如下:

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.util.Iterator;

public class SortReducer extends Reducer<IntWritable, IntWritable, Text, IntWritable> {
    private Text rankText = new Text();
    private int counter = 1;

    @Override
    protected void reduce(IntWritable key, Iterable<IntWritable> values, 
                         Context context) throws IOException, InterruptedException {
        
        Iterator<IntWritable> iterator = values.iterator();
        while (iterator.hasNext()) {
            iterator.next(); // 消耗值但不使用
            rankText.set(Integer.toString(counter));
            context.write(rankText, key);
            counter++;
        }
    }
}

部署,制作jar包,先上传输入文件到hdfs,然后上传到集群(放在/root)。这里同样需要注意Java编译版本问题。

图21 制作jar包

图22 上传集群/root

(3)运行程序。命令行输入

hadoop jar sort.jar SortDriver
hadoop fs -cat /output/sort/part-r-00000

运行,命令行输入hadoop fs -ls /output和hadoop fs -cat /output/sort/part-r-00000验证结果。

图31 验证结果

图32 浏览器查看信息

至此所有任务基本完成,相信大家也已经掌握了mapreduce编程的技巧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值