注:以下内容来源于互联网,由自己整理,作为读书笔记使用。
1.HDFS
HDFS文件指令和centos的很像
在创建目录时,若提示节点处于安全状况,只需要关闭安全模式即可。
hadoop dfsadmin -safemode leave
显示当前目录文件: hadoop fs –ls 目录 目录一定不要忘了,即使是当前目录。
元数据的管理(数据在datanode上存储的映射关系):
Namenode:nn datanode:dn
上传文件(以三个block副本为例):客户端将数据切成一个一个的block,每个block切分由客服端完成,写完一个再写一个,直至写完。副本由datanode完成,且和客户端是异步的。在副本的复制中,若一台datanode复制block没有成功,会报告给第一个namenode,在由他传给Namenode,namenode负责找另外的机器存储这个副本,全程不需要客户端负责副本的复制。
对于小文件,也切分成一个一个的block,虽然对dataname影响较小,但是会增加namenode的映射内存,但是实际存储的东西并没有很多,不好。
Namenode如何快速响应海量用户的存储和查询:
元数据就是映射关系。存到文件?文件坏了怎么办;放电脑内存?断电怎么办。下一张图。
Fsimage 用来存储所有的block和地址映射,是一个相对较大的文件。Edits是一个小文件,每存储一个block就映射存到edits中,当edits快满时,通过secondnode比对并将加到fsimage中。地址映射(即 元数据管理)大体就是这样,各个组件的作用也说明了。
小提示:图形界面至少2个G, 若要流畅,可以设大一点。命令行可以设为512M。启动后启动图形界面:startx ,比init5块,因为不是重启。
2.Yarn 资源调度
1. hadoop 执行wordcount程序的job.waitforcompletion()后,会产生一个Runjar进程,并报告给Yarn中资源管理器(RESOURCE MANNAGER: RM).
2. RM给这个程序一个工作号(jobID)和一个目录(用于存储该工作所需的一切,具体的虚拟目录在hdfs上,见图,其实这个目录分布在各个机器上的容器中)。
3. Runjar资源提交好后,汇报给RM,RM将这个工作加入消息队列,等NODE MANAGER 来领取任务(多个)。
4. NODE MANAGER 领取任务后再自身机器内分配容器存储本次job所需要的一切,如CPU,i/o ,磁盘等等。文件资源,jar包,配置这些也会拿过去。
5. Yarn是不懂MapReduce的,这时候会启动MRAPPMaster管理具体的MapReduce程序的执行,监控,并报告给RM等。
6. MRAPPMaster 先去RM领取一些信息,如文件在哪儿等等;然后开辟一个yarnchild进程,用于执行map程序;执行完成后再开辟一个yarnchild进程,用于执行reduce程序。在这过程中有进程执行完毕会自动关闭,资源被RM回收。
7. MapReduce全部执行完成后,MRAPPMaster向RM报告并注销自己。
所以,里面的yarnchild进程是动态产生的。
下面是wordcount程序运行前后进程的变化,就是图中的进程。
为什么同一段代码在本地的IDE执行就是运行的是单机版,而在centos提交就是在在集群上运行呢?
这是因为hadoop根据配置文件来判断执行在哪里执行。如下图完全一样的代码在本地执行,就是本地单机运行,但仍可以取服务器 存 取 数据,但是没有去hadoop集群执行。而当这个包打包成jar包,扔到centos服务器上就是在集群上执行。
注:其实只需要准备输入文件,不需要准备输出目录。因为 MapReduce 程序的运行,其输出目录不能是已存在的,否则会抛出异常。这是为了避免数据覆盖的问题。请看《Hadoop权威指南》。
下面是编写 wordcount 程序中的流程:
1. 环境配置:
第一次写的时候,要在工程右键上添加hadoop的依赖(hadoop相关的jar包),在 share目录下去找 对应的。
注意:org.apache.hadoop.mapreduce 在 hadoop-2.5.1\share\hadoop\mapreduce 目录下导入jar包
eclipse 设置背景等:https://blog.csdn.net/java_xiaopan/article/details/53032728
2. wordcount程序编写:
大体流程是需要编写一个map类,用来在datanode中去对数据进行处理,编写一个reduce类,用来对各个map的结果进行汇总,再写一个Runner主类,设置一系列固定流程。
Map 类编写:
'''
数据格式:
hello ddd
hello ssss
hello ddd
hello ssaa
'''
package wordcount;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
//框架自动把类型给你转换好,这样map和reduce才不会解析错误。另,传给reduce的文件格式也要指定好。
//4个泛型中,前两个是指定mapper输入数据的类型,KEYIN是输入的key的类型,VALUEIN是输入的value的类型
//map 和 reduce 的数据输入输出都是以 key-value对的形式封装的
//默认情况下,框架传递给我们的mapper的输入数据中,key是要处理的文本中一行的起始偏移量(即行数为key),这一行的内容作为value
// LongWritable 表示的是数字,Text 是文本
//前两个是Mapper输入数据: 偏移量(第几行) Text 该行的内容
//后两个是Mapper输出数据:某个统计的词(Text) LongWritable 有多少个
//不论输入和输出都是 一对键值,且用hadoop封装的格式进行传输(数据流)。
public class WCMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
//网络中的传输是用序列化进行
//hadoop 实现了一套序列化的方法,传输效率更高。只需要调用序列化接口。
// text就是string的封装。
//mapreduce框架每读一行数据就调用一次该方法
@Override
protected void map(LongWritable key, Text value,Context context)
throws IOException, InterruptedException {
//具体业务逻辑就写在这个方法体中,而且我们业务要处理的数据已经被框架传递进来,在方法的参数中 key-value
//key 是这一行数据的起始偏移量 value 是这一行的文本内容
//将这一行的内容转换成string类型,在网络中传输是字节流传输,进入某个节点计算机就要反序列化为具体的内容对象
String line = value.toString();
//对这一行的文本按特定分隔符切分
String[] words = StringUtils.split(line, " "); //自带工具
//遍历这个单词数组输出为kv形式 k:单词 v : 1
for(String word : words){
context.write(new Text(word), new LongWritable(1)); //这样指定类型传输
// context 框架的包,传给它自动将内容传出去。
}
}
}
reduce 类编写(汇总):
package wordcount;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class WCReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
//框架在map处理完成之后,将所有kv对缓存起来,进行分组,然后传递一个组<key,valus{}>,调用一次reduce方法
//如 <hello,{1,1,1,1,1,1.....}>
@Override
// 下面也是重写方法 ,Iterable 迭代器,即对list进行处理。
protected void reduce(Text key, Iterable<LongWritable> values,Context context)
throws IOException, InterruptedException {
long count = 0;
//遍历value的list,进行累加求和
for(LongWritable value:values){
count += value.get();
}
//输出 这一个单词的统计结果
context.write(key, new LongWritable(count)); //传输的时候一定要严格按照hadoop格式进行规范。
}
}
Runner主类:
package wordcount;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;
/**
* 用来描述一个特定的作业
* 比如,该作业使用哪个类作为逻辑处理中的map,哪个作为reduce
* 还可以指定该作业要处理的数据所在的路径
* 还可以指定该作业输出的结果放到哪个路径
* ....
*
*/
public class WCRunner {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job wcjob = Job.getInstance(conf);
//设置整个job所用的那些类在哪个jar包
wcjob.setJarByClass(WCRunner.class);
//本job使用的mapper和reducer的类
wcjob.setMapperClass(WCMapper.class); //本包目录下的mapper类
wcjob.setReducerClass(WCReducer.class);
// 上三行将本job的类生成 jar包,wcjob.setReducerClass(classname.class)
//指定reduce的输出数据kv类型:key和value的输出格式
wcjob.setOutputKeyClass(Text.class);
wcjob.setOutputValueClass(LongWritable.class);
// 若map和reduce的格式一样,则下面的指定mapper可以不写
//指定mapper的输出数据kv类型:key和value的输出格式
wcjob.setMapOutputKeyClass(Text.class);
wcjob.setMapOutputValueClass(LongWritable.class);
// 下面的路径都是hdfs的虚拟路径,实际路径不清楚
//指定要处理的输入数据存放路径
FileInputFormat.setInputPaths(wcjob, new Path("hdfs://192.168.2.103:9000/example/input/word.txt"));
// 在实际运行的时候要记得有对应的文件目录,output必须自动生成。
//指定处理结果的输出数据存放路径
FileOutputFormat.setOutputPath(wcjob, new Path("hdfs://192.168.2.103:9000/example/output/"));
//将job提交给集群运行 ,true表示显示执行过程。
wcjob.waitForCompletion(true);
}
}
上传数据到输入数据目录;
执行,去对应的wordcount.jar包目录:
Hadoop jar wordcount.jar 程序中包名.wordcountRunner
Yarn 的意义:
不论是spark还是其他大数据工具,yarn都是通用的,只需要提供一个借口即可,即下图中MRAppMaster替换成对应的即可,让它启动去读程序中的内容。另外,hdfs也是通用的,因为这是存储。
注意:MRAppMaster是随机在一台 node manager 上启动的,并不是每台。
所以,不论是何种大数据工具,底层可以直接用hadoop的hdfs来存储数据,用yarn来资源调度,上层用对应大数据工具即可实现一种新的大数据工具,如 spark。