hadoop文件
namenode: 存储元数据, 被分块保存的数据的信息,如大小,位置 datanode: 存储被分块的数据, path->hadoop/data/dfs/data(本地的实际地址) hadoop会建立一个虚拟的文件目录工客户端访问(hdfs://ip:9000/)
HDFS实现思想
1. hdfs是通过分布式集群来存储文件, 为客户端提供了一个便捷的访问方式,就是一个虚拟的目录结构 2. 文件存储到hdfs集群中去的时候是被切分成block的 3. 文件的block存放在若干台datanode节点上 4. hdfs文件系统中的文件与真实的block之间有映射关系, 由namenode管理 5. 每一个block在集群中会存储多个副本,以提高数据的可靠性与访问的吞吐量
hadoop的namenode
1. 客户端上传文件时, NN首先往edits.log文件记录元数据操作日志 2. 客户端开始上传文件,完成后返回成功的信息给NN, NN就在内存中写入上传的元数据 3. 再往fsimage(元数据的持久化地方)里写入, 当edits.log写满时/到达一定时间(默认3600s). SecondaryNameNode负责这部分操作. 元数据地址: /usr/hadoop/tmp/dfs/name/current
HadoopAPI
RPC远程通讯, 利用远程通讯调用hadoop原生的方法,拿到返回结果(通过fs) hadoop里的通讯走RPC
Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf); Path src = new Path('hdfs"//master:9000/test.txt'); /*hadoop文件地址*/ FSDataInputStream io = fs.open(src); FileOutputStream os = new FileOutputStream('/localpath'); IOUtils.copy(in, os);
FileSystem fs = null; public void init() throws Exception{ //读取classpath下的xxx.site.xml配置文件,并解析其内容,封装到conf对象中 Configuration conf = new Configuration(); // 也可以在代码中对conf中的配置信息进行手动设置,会覆盖配置文件中读取的值 conf.set("fs.defaultFS", 'hdfs://master:9000/'); fs = FileSystem.get(new URi('hdfs://master:9000/'), conf, 'hadoop') /*指定登录user, 与集群进行通讯*/ } fs.copyFromlocalFile(new Path(''), new Path('hdfs://master:9000/')) /*传入本地路径, 目标路径*/
MapReduce
mapper->对数据进行切片,计算,返回{k:v}({词: 1})格式 reduce->对map的结果, 统计key的数量 Mapper: 处理具体文本,发送结果 Reducer: 合并各个Mapper发送过来的结果 Job: 制定相关配置,框架,[将相同的mapper结果发送到同一个reduce上处理,或依照逻辑不同志发送到一个reduce处理] mapreduce的程序可以在任何地方运行,它只是一种统计逻辑,文件的读写可以在任何地方
mapper实现
package cn.itcast.hadoop.mr.wordcount; import java.io.IOException; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.util.StringUtils; // <LongWritable, Text, Text, LongWritable> 4个泛型中,前两个是指定mapper输入数据的类型,这是hadoop自定义的序列化数据类型,便于传输 // map和 reducer的输入和输出都是key-value对的形式 public class WCMapper extends Mapper <LongWritable, Text, Text, LongWritable> { // 每读一次数据,就调一次这个方法 // 默认情况下。框架输入的我们mapper的输入数据中,key是要处理的文本中的一行的起始偏移量, 内容就是value @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException{ //具体业务逻辑就像和在这里, 传入数据就是 key, value //将这一行的内容转化成string类型 String line = value.toString(); // 对这一行的文本按特定分隔符切分 String[] words = StringUtils.split(line, ' '); // 遍历这个单词数组, 输出为kv形式 k:单词 v:1 for (String word : words) { //hadoop的输出类型 context.write(new Text(word), new LongWritable(1));}}}
reduce实现
package cn.itcast.hadoop.mr.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,vakue>,调用一次reduce方法 //参数<word, {1,1,1,1,1,.....}> 经过shuffle后的键值 @Override protected void reduce(Text Key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { long count = 0; for (LongWritable value:values) { count += value.get(); //拿到LongWritable的值 } context.write(Key, new LongWritable(count));}}
job实现
job用于描述一个特定的作业,比如作业使用哪一个map,reduce. 还可以指定该作业要处理的数据所在的路径, 作业的输出的结果放到哪个路径下. package cn.itcast.hadoop.mr.wordcount; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.io.Text; //import com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider.Text; public class WCRunner { public static void main(String[] args) throws Exception{ Configuration conf = new Configuration(); Job wcJob = Job.getInstance(conf);//获取Job类 //设置整个job所有的哪些类在哪个jar包 wcJob.setJarByClass(WCRunner.class); //本job使用的mapper和reducer类 wcJob.setMapperClass(WCMapper.class); wcJob.setReducerClass(WCReducer.class); // 指定reduce的输出数据kv类型 wcJob.setOutputKeyClass(Text.class); wcJob.setOutputValueClass(LongWritable.class); // 指定mapper的输出数据kv类型 wcJob.setMapOutputKeyClass(Text.class); wcJob.setMapOutputValueClass(LongWritable.class); //指定要处理的输入数据存放路径(文件所在文件夹) FileInputFormat.setInputPaths(wcJob, new Path("/wc/srcdata/")); //指定处理结果的输出数据存放路径 FileOutputFormat.setOutputPath(wcJob, new Path("/wc/output")); //将job提交给集群运行, 并指定在控制台打印过程 wcJob.waitForCompletion(true);}}
Yarn, 资源调度
Yarn工作流程
1. wcJob.waitforCompleition启动一个RunJar进程,这个进程向RM申请执行一个Job 2. RM 返回一个Job相关资源的路径staging-dir,和为Job产生的jobID 3. RunJar提交资源到 HDFS的 staging-dir 上 4. RunJar提交资源完毕之后,上报RM 提交资源完毕 5. RM下个Job加入RM中的任务队列中 6. 各个Node Manager通过通信,从RM的任务队列中领取任务 7. 各个Node Manager初始化运行资源的容器,从staging-dir上面拉取资源 8. RM选择一个Node Manager 启动MRAppMaster 来运行map reducer 9. MRAppMaster向RM注册 10. MRAppMaster启动Mapper任务 11. MRAppMaster启动Reducer任9务 12. 任务完成后,向RM注销自己
实现hadoop的序列化接口
在map的时候, 传递自定义数据类型, 需要实现hadoop相应的序列化接口