今天可算是完成了一个小项目,虽然看起来很简单,但麻雀虽小,五脏俱全,这个小项目也就是把整个框架的建构与测试走一遍。具体的话包括:服务器上建立测试文件,在Windows下写代码,Win下测试,上传服务器,在服务器上测试。
一路上真的遇到了很多坑,包括环境配置和一些代码问题,更主要的是eclipse本地连接hdfs产生了比较多报错。这两天也一直在查资料,看了很多的博客才解决,总之,解决了就是好事,希望你们能少走一点弯路
服务器上传文件
在服务器上打开Hadoop-2.7.3路径,直接在hadoop根目录下新建一个文本文件。
[songjian@learn hadoop-2.7.3]$ vi 1.txt
在文本文件中随便输入什么单词。这里就直接放图片了。
那么接下来就是上传,直接在Linux命令行敲代码就行。
[songjian@learn hadoop-2.7.3]$ bin/hdfs dfs -put 2.txt /
检查一下,打开hdfs://192.168.6.129:50070。没凑,已经上传成功了。
环境配置
那么整个项目下来比较麻烦的就是环境了,装了很多之前没有搞好的东西才能完成,接下来一步一步做好,别漏了,否则你也要搞很久。
首先,把你在服务器上的hadoop-2.7.3整个文件夹通过FileZila传到本地(这里的话一定要把基本的软件安装好,例如FileZila,SecureCRT),记得放在一个比较好的地方,因为以后会用。我放在了D盘,新建了一个文件夹BigData里面,如图:
以后可以把有关于BigData的东西丢在这个文件夹里面,方便管理。
然后配置hadoop环境变量,这个就跟配置JAVA环境变量一样。我这里就直接放图片了,具体的可以参考一下csdn,很多讲解。
当你这两步都完成以后,可以检查一下自己是否配置完成。打开cmd,输入hadoop,如果出现一下画面即是成功配置!
但是!注意了哈!一开始肯定是不行的,你肯定肯定会遇到一个问题!!!看下图!
1号坑:
Error: JAVA_HOME is incorrectly set.
解决方式:
1、首先检查你的JAVA配置有没有问题!在cmd上输入java -version,若出现下图则说明没问题。
2、如果没有出现的话,自己去百度查怎么完成java的环境配置。如果你的JAVA配置没有问题的话,请看第3步。
3、在刚刚放好的hadoop路径中找到这个文件:hadoop-2.7.3\etc\hadoop\hadoop-env.cmd(你的文件在哪就去哪找)。打开文件编辑(这里的话推荐使用NotePad++,因为这个也可以直接连接服务器,很方便)。
这里把自己配置好的JAVA路径改一下,JAVA路径可以在环境变量中找到,如下图:
那么复制下来黏贴就能解决吗?没那么简单!!
注意:因为Program Files中存在空格,所以出现错误。因此,PROGRA~1代替Program Files即可。可以看看前两张图。改完就可以了!
下一步:eclipse连接Hadoop
1、安装Myeclipse(这个就不说了),具体的百度csdn去查,不难。
2、将hadoop-eclipse-plugin-2.6.0.jar(这个jar百度下应该是有的)这个jar放在Myeclipse的安装目录的plugins,如图:
3、在Windows解压hadoop-2.5.0.tar.gz,然后配置hadoop的环境变量(贴两个图上来,就不具体说了,这不就跟配置jdk是一样的嘛~),这个前面刚刚搞定了。
4、打开Myeclipse,请看!
这里点击下面的Map/Reduce Location,在下面的空白处点击右键,添加新的Location。
填好以后,点击小象按右键,Reconnect。成功连接以后就会出现下图!
好,到这里!!!坑来了。
2号坑:
点击tmp,可能会遇到下面这段话:
Permission denied: user=lenvol, access=WRITE_EXECUTE, inode="/input":hadoop:supergroup:rwxr-xr-x。
解决方式:
这个情况是Hadoop权限控制问题。要调整一下权限。
在hadoop的 hdfs-site.xml 配置文件中取消权限校验,即加入以下配置:
<property> <name>dfs.permissions</name> <value>false</value> </property>
如下图:
配置好这些东西,要记得重启服务器,在尝试重新连接。连接成功后就可以打代码啦!
最最最后配置的就是maven,用于管理jar包,特别方便,这个自己下载就好,常规,没有什么坑。
搞完了以后,就可以开始写代码啦!!!
代码
这边的话直接上代码,具体的学习内容出门右拐大数据去学习去!
package com.songjian.learn.mapreduce;
import java.io.IOException;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class MyMapReduce {
//1、自己的map类
//继承Mapper类,<KEYIN, VALUEIN, KEYOUT, VALUEOUT> 输入的key,输入的value,输出的key,输出的value
public static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
//创建一个IntWritable类型的对象,给定值为1
IntWritable i = new IntWritable(1);
Text keystr = new Text();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
String line = value.toString();
//传入每一个map方法的key和value做打印
System.out.println("key : "+key.get()+"--------- value : "+line);
String [] strs = line.split(" ");
for (String str : strs) {
//每一次循环遍历到一个单词就要输出到下一个步骤
keystr.set(str);
System.out.println("map的输出:key : ("+str+",1)");
context.write(keystr, i);
}
/* StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
keystr.set(itr.nextToken());
context.write(keystr, i);
}*/
}
}
//2自己的reduce类
// reduce类的输入,其实就是map类中map方法的输出 输入key 输入value 输出key 输出value
public static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
IntWritable countwritable = new IntWritable();
@Override
//Map类的map方法的数据输入到Reduce类的group方法中,得到<text,it(1,1)>,再将这个数据输入reduce类到reduce方法中
protected void reduce(Text inputkey, Iterable<IntWritable> inputvalue,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
//得到了key
String key = inputkey.toString();
//迭代Iterable,把每一个值相加
int count = 0;
//循环遍历迭代器中的所有值,做相加
for (IntWritable intWritable : inputvalue) {
count = count + intWritable.get();
}
//把值设置到IntWritable,等待输出
countwritable.set(count);
System.out.println("reduce输出结果:key : "+key+" , "+ count);
context.write(inputkey, countwritable);
}
}
//3运行类,run方法,在测试的时候使用main函数,调用这个类的run方法来运行
/**
*
* @param args 参数是要接受main方法得到的参数,在run中使用
* @return
* @throws Exception
*/
public int run(String[] args) throws Exception {
//hadoop的配置的上下文!
Configuration configuration = new Configuration();
//通过上下文,构建一个job实例,并且传入任务名称,单例!
Job job = Job.getInstance(configuration, this.getClass().getSimpleName());
//这参数必须添加,否则本地运行没有问题,服务器上运行会报错
job.setJarByClass(MyMapReduce.class);
//设置任务从哪里读取数据?
//调用这个方法的时候,要往args中传入参数,第一个位置上要传入从哪里读数据
Path inputpath = new Path(args[0]);
FileInputFormat.addInputPath(job, inputpath);
//设置任务结果数据保存到哪里?
//调用这个方法的时候,要往args中传入参数,第二个位置上要传入结果数据保存到哪里
Path outputpath = new Path(args[1]);
FileOutputFormat.setOutputPath(job, outputpath);
//设置mapper类的参数
job.setMapperClass(MyMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setNumReduceTasks(2);
//设置reduce类的参数
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// submit job -> YARN
boolean isSuccess = job.waitForCompletion(true);
return isSuccess ? 0 : 1;
}
public static void main(String[] args) {
args= new String[]{
"hdfs://192.168.6.129:8020/1.txt",
"hdfs://192.168.6.129:8020/output"
};
MyMapReduce mr = new MyMapReduce();
try {
int success = -1;
success = mr.run(args);
System.out.println("success:"+success);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
接下来就是坑了。
3号坑:
Exception in thread "main" java.lang.NullPointerException atjava.lang.ProcessBuilder.start(Unknown Source)
An internal error occurred during: "Map/Reducelocation status updater".java.lang.NullPointerException
解决方式:
如果出现这样的问题,就是上文的配置你没配置好,好好检查下!然后在hdfs上创建目录,上传文件试试,如果配置好了,应该不会有这样的问题。
4号坑:
Failed to locate the winutils binary in the hadoop binary path java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
解决方式:
这里其实是因为在Windows下面运行mr代码必须有个文件叫做winutil.exe,默认解压的hadoop的bin目录下是没有的,自己下载一个然后放到hadoop目录的bin当中,程序会根据HADOOP_HOME找到bin目录下面的winutil.exe,但是有时候其实你都配置好了,它还报这个错,这时候就要修改源码了(看看源码是哪里获取的,你去手动写一个你正确的路径)。来看看图:
就是这个属性,修改下!!那怎么改,有个很简单的方法,Ctrl+A(全选),然后Ctrl+C(复制),把整个类复制下来,然后看下图!
点击画红圈的地方,Ctrl+V粘贴进去,他会自动生成想源码那样的包,然后直接改这个生成的java类,把350行改成:
完成!
5号坑:
Exception in thread "main"java.lang.UnsatisfiedLinkError:org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)
解决方式:
修改下源码就可以搞定了,其实没有必要把源码包里的.class文件反编译出来,修改,再重新打成jar包,你只要直接点开NativeIO这个类,然后Ctrl+A全文复制,再左键单击你的项目的文件夹(如下如点选src/main/java),最后Ctrl+V这样就会直接生成对应的包名和类。
接着修改下570行的return,注释掉原来的return,直接让代码return true就可以了。如下图:
在本地测试代码。成功运行就会出现一下的结果(这里比较长,就截一部分图):
红色不是错误,而是日志文件,可以添加也可以不添加,没有也没关系。如果要添加
那么本地测试就完全完成了。接下来就是打包jar包。
打包jar包
这个操作如果是经常写项目就很简单了。没有什么坑,直接export就行。
然后跟着next就行了。
在服务器上测试
最后一步,直接把jar包通过FileZila传到服务器。
最后就是测试程序,敲代码。
[songjian@learn hadoop-2.7.3]$ bin/yarn jar may.jar /1.txt /output
然后耐心等一等,就可以跑完啦!!
打个总结
那么综上所述啦,收获不少,这两天挺烦的,遇到各种头疼的bugs,不过还好都解决了。剩下的话其实程序并没有写的很完整,到时候还会在大数据的推文中继续学习。
代码请前往GitHub或者直接在这里复制。