Hadoop之MapReduce的Java实现

字数 1449阅读 869

今天将为大家演示一下,Hadoop中MR用Java是如何编码实现的。

1.环境准备

1.1 需要把下载的hadoop包解压到windows目录下,注意不要有空格目录或者中文字符

image.png

1.2 配置环境变量

配置系统环境变量HADOOP_HOME,指向hadoop安装目录(如果你不想招惹不必要的麻烦,不要在目录中包含空格或者中文字符) 。把HADOOP_HOME/bin加到PATH环境变量(非必要,只是为了方便)
以下截图为windows下配置:


image.png

image.png

1.3 windows例外配置1

如果是windows:在E:\bigdata\hadoop\etc\hadoop目录下的hadoop-env.cmd文件中修改JAVA_HOME环境变量(注意JDK安装路径不能含有空格,如:C:\Java\jdk1.8.0_11)


image.png

1.4 windows例外配置2

如果是在windows下开发,需要添加windows的库文件,把网盘中共享的bin目录覆盖HADOOP_HOME/bin(已经转移到CSDN,下载),如果还是不行,把其中的hadoop.dll复制到c:\windows\system32目录下,可能需要重启机器。

1.5 建立新项目,引入hadoop需要的jar文件

2.代码

WordMapper类:

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


public class WordMapper extends Mapper<LongWritable,Text, Text, IntWritable> {

    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
            throws IOException, InterruptedException {

       //接收到split中的输出结果作为map的输入 
        String line = value.toString();
        String[] words = line.split(" ");
       
        //放到list中,每个text统计加1,作为map的输出
        //写到context上下文环境中,输出到下一个执行过程shuffle
        for(String word : words) {
            context.write(new Text(word), new IntWritable(1));
        }
    }

}

WordReducer类

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

public class WordReducer extends Reducer<Text, IntWritable, Text, LongWritable> {

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values,
            Reducer<Text, IntWritable, Text, LongWritable>.Context context) throws IOException, InterruptedException {
        long count = 0;
        
        //此时的输入是shuffle的输出
        //输入的key是字符串,输入的value是shuffle派发过来的是一个个list
        for(IntWritable v : values) {
            count += v.get();
        }
        context.write(key, new LongWritable(count));
    }

}

3.运行

3.1 直接本地运行

直接本地输入输出,代码Test

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
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 Test {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();

        Job job = Job.getInstance(conf);

        job.setMapperClass(WordMapper.class);
        job.setReducerClass(WordReducer.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        FileInputFormat.setInputPaths(job, "c:/bigdata/hadoop/test/test.txt");
        FileOutputFormat.setOutputPath(job, new Path("c:/bigdata/hadoop/test/out/"));

        job.waitForCompletion(true);
    }
}

直接本地运行结果:

本地运行结果1.png
本地运行结果2.png

3.2 把hdfs中的文件拉到本地来运行

需要Test中的以下代码:

FileInputFormat.setInputPaths(job, "hdfs://master:9000/wcinput/");
FileOutputFormat.setOutputPath(job, new Path("hdfs://master:9000/wcoutput2/"));

注意这里是把hdfs文件拉到本地来运行,如果观察输出的话会观察到jobID带有local字样
同时这样的运行方式是不需要yarn的(自己停掉yarn服务做实验)


hdfs文件拉到本地来运行.png

如上图,此时还是在本地运行mapReduce程序

3.3 在远程服务器执行

修改以下代码:

//配置了fs.defaultFS选项,则下面的setInputPaths等配置不需要再配置前缀
conf.set("fs.defaultFS", "hdfs://192.168.56.200:9000/");
conf.set("mapreduce.framework.name", "yarn");//MapReduce运行环境为yarn
conf.set("yarn.resourcemanager.hostname", "192.168.56.200");//yarn的recourseManager的主机名
conf.set("mapreduce.app-submission.cross-platform", "true");//表示MapReduce是跨平台运行的
FileInputFormat.setInputPaths(job, "/wcinput/");
FileOutputFormat.setOutputPath(job, new Path("/wcoutput3/"));

此时运行,则会出现权限问题,如下图:


image.png

如果遇到权限问题,有三种方法:

  1. 在系统的环境变量或java JVM变量里面添加HADOOP_USER_NAME,这个值具体等于多少看自己的情况,以后会运行HADOOP上的Linux的用户名。(修改完重启eclipse,不然可能不生效)
  2. 将当前系统的帐号修改为hadoop(运行HADOOP上的Linux的用户名,本例为root)
  3. 使用HDFS的命令行接口修改相应目录的权限,hadoop fs -chmod 777 /user,后面的/user是要上传文件的路径,不同的情况可能不一样,比如要上传的文件路径为hdfs://namenode/user/xxx.doc,则这样的修改可以,如果要上传的文件路径为hdfs://namenode/java/xxx.doc,则要修改的为hadoop fs -chmod 777 /java或者hadoop fs -chmod 777 /,java的那个需要先在HDFS里面建立Java目录,后面的这个是为根目录调整权限。

这里采用第一种方法配置执行时的虚拟机参数-DHADOOP_USER_NAME=root


image.png

也可以将hadoop的四个配置文件拿下来放到src根目录下,就不需要进行手工配置了,默认到classpath目录寻找
或者将配置文件放到别的地方,使用conf.addResource(.class.getClassLoader.getResourceAsStream)方式添加,不推荐使用绝对路径的方式
通过以上配置以后,就可以运行MapReduce了,如下图所示:


image.png

上图就表示,已经在远程服务器中执行MapReduce程序了(jobid没有local前缀了),但是问题又来了,执行到后面出现以下找不到类的情况:

java.lang.ClassNotFoundException: Class org.lyx.mapreduce.WordMapper not found
image.png

这是因为服务器上没有WordMapper这个类,这时候需要两个步骤的操作:
1.打包整个MapReduce工程为一个jar包,eclipse的话直接export(java工程),如果是maven工程,直接使用Package打包成jar。如下图所示(idea):


image.png

打包完成如下图所示:


image.png

2.加入以下代码:
//配置此工程打好的jar包路径,建议使用相对路径
conf.set("mapreduce.job.jar", "target/mapreduce-0.0.1-SNAPSHOT.jar");

或者

job.setJar("target/mapreduce-0.0.1-SNAPSHOT.jar");

加入以上代码后,需要重新打包。

此时再次运行即可在远程服务器中执行MapReduce程序,执行结果如下图所示:


远程运行结果.png
远程运行结果.png

在web网页中观察结果如下:

hdfs中的情况:


image.png

在yarn中的情况如下:


image.png

4.Maven配置

建立maven-hadoop项目

4.1 pom.xml

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
    </dependencies>

4.2 log4j

配置log4j.properties,放到src/main/resources目录下
log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n

5.MapReduce运行程序常见错误以及解决办法:

错误1.
Exit code: 1
Exception message: /bin/bash: 第 0 行: fg: 无任务控制
Stack trace: ExitCodeException exitCode=1: /bin/bash: 第 0 行: fg: 无任务控制
在客户端配置文件(mapred-site.xml)中添加如下属性:

 <property>
        <name>mapreduce.app-submission.cross-platform</name>
        <value>true</value>
 </property>

注意:必须添加到Hadoop程序读取的客户端本地配置文件中,添加到客户端Hadoop安装路径中的“core-site.xml”,“mapred-site.xml”等文件中不起作用
或者在代码中加入:

conf.set("mapreduce.app-submission.cross-platform", "true");//表示MapReduce是跨平台运行的

6. eclipse插件

插件下载:http://download.csdn.net/download/landy8530/10106631
安装hadoop-eclipse插件,直接把hadoop-eclipse-plugin-2.7.3.jar文件放入到eclipse安装目录下的plugins目录即可(为了安全起见,可以先把eclipse先备份一下),重启eclipse,open perspective显示map/reduce即可。

eclipse MR插件.png

然后在
image.png

中新增一个hdfs的配置即可看到hdfs中的文件系统。


image.png

image.png

7.延伸阅读

  1. Hadoop安装与集群配置
  2. Hadoop基本知识点之HDFS
  3. Hadoop之HDFS的Java实现
  4. Hadoop之YARN的安装与测试

事例完整代码:https://github.com/landy8530/mapreduce

推荐阅读更多精彩内容

  • 首先,我们在使用前先看看HDFS是什麽?这将有助于我们是以后的运维使用和故障排除思路的获得。 HDFS采用mast...
  • hadoop是什么?HDFS与MapReduceHive:数据仓库,在HDFS之上,后台执行,帮你执行。faceb...
  • Hadoop的编程可以是在Linux环境或Winows环境中,在此以Windows环境为示例,以Eclipse工具...
  • 随着全球经济的不断发展,大数据时代早已悄悄到来,而Hadoop又是大数据环境的基础,想入门大数据行业首先需要了解H...
  • 当数据量增大到超出了单个物理计算机存储容量时,有必要把它分开存储在多个不同的计算机中。那些管理存储在多个网络互连的...