Hadoop入门3-MR介绍

1.MapReduce原理    **

2.MapReduce执行过程    **

3.数据类型与格式        ***

4.Writable接口与序列化机制    ***

5.MapReduce的执行过程源码分析

首先第一个问题求和:1+5+7+3+4+9+3+5+6      

Map:        1+5+7        3+4+9        3+5+6

Reduce:        13        +    16        +    14

1G的数据,放在内存只有1M上的计算机上,将1G切分成1024M,然后一个一个进行计算。并行地计算。将一个任务拆分成多个数据分给不同的计算机进行计算,然后将结果进行汇总交给Reduce进行计算。

MapReduce是一种分布式计算模型,主要由Map和Reduce两个阶段组成。用户只需要实现map()和reduce()两个函数,即可实现分布式计算,非常简单。

这两个函数的形参是key、value对,表示函数的输入信息。

自己设计一个MapReduce框架:Hadoop1:老大:JobTracker(只运行mapreduce),小弟TaskTracker。也是一个跟踪者,手下也有内容。

在Hadoop2后改名:JobTracker/RM:    TaskTracker/NodeManager     TaskTracker里面监控着一些子进程,包括Map.Reduce等内容。 

一些流程:

        首先将计算的数据上传到HDFS中,其次有一个已经打好的程序,jar包。为了不让RM存储了大量jar包,领任务时RM发送给NM(这种方式不好):RM压力太大。另外一种更好的方式:NM发送任务,发送心跳机制给RM,询问是否有任务,接着再从HDFS里面读数据,进行MapReduce的进行之后再返回给HDFS就OK了。jar包:存放到HDFS中去。

流程:Client发送一个请求,告诉给了RM,包括jar包的信息,做哪些操作,接着NM汇报信息领取任务,NM再从HDFS中下载jar包和数据,处理完了之后再反馈给HDFS就OK了。

一个切片,块对应一个Mapper,然后Mapper的输出作为reduce的输入,完成后再返回给HDFS就OK了。逻辑切分:虚拟的切分。物理切分:实际地被切分了,一块一块的。在块的基础之上打标记,一个Mapper读取64M的数据。

Mapper的小名:K1,V1。这是mapper的输入   K2V2:mapper的输出  K3V3:Reducer的输出。        Shuffle:很重要。暂时认为Map就算完后给Reduce。shuffle:排序,分区,分组。

将Mapper给Reduce之前进行了一个分组的操作,根据K2来进行分组,把V2放到一个集合中去了。牢记K1V1,K2V2,K3V3

Map执行过程:

        map读取数据,一个inputsplit对应一个map,解析成key,value对。一行内容解析成一个key-value。一个key,map执行一次map函数。十分重要的内容。

        写自己的逻辑,对输入的key,value处理,转换成新的key,value输出。

        对输出的key,value进行分区。

        对不同分区的数据,按照key排序分组,相同key的value放到一个集合中。

        分组后的数据进行规约。

Reduce执行过程:

       对多个map任务的输出,按照不同的分区,通过网络copy到不同的节点。

       对多个map任务的输出进行合并,排序。写reduce函数自己的逻辑,对输出的key,value处理,转换成新的key,value输出。

       把reduce的输出保存到文件中。

WordCout流程:入门级程序。

  HDFS组件:里面存放了文件内容。       ->解析成key,value                   Map阶段(字符的偏移量)

                                                                                                <0,"Hello,tom">

                                                                                               <10,"hello jerry">

                                                                                               <22,"hello kitty">

                                                                                               <34,"hello world">

                                                                                               <46,"hello tom">

                一边解析一边进行执行。每一行都会执行一个map函数:

map(){

        //0

        //int key=k1;

        //"hello tom"

    String value=v1

    String [] words = value.split(" ");

    for(String w:words){

            context.write(w,1);

    }

}

                                     Shuffed:阶段,分区,排序,分组等内容

                                                                    Reduce阶段(按照默认字典顺序)

                                                                               

                                                                               

                                                                                 

                                                                                

                                                                                 

    执行reduce(){

            String key = k2;

            int counter=0;

            for(int i:v2s){

              counter+=i;

        }

    context.write(key,counter);

}

Map-Reduce基础及代码编写。    

Runnable Jar file:Java -jar执行    JAR file:Hadoop -jar         

总结:编写MapReduce:

    1.分析具体的业务逻辑,确定输入输出数据的样式 。各种各样的输入输出。

    2.自定义一个类,这个类要继承Mapper这个类。重写map()方法,实现具体业务逻辑,将新的key,value输出。  

     3.自定义一个类,这个类要继承Recuer这个类。重写reduce()方法,实现具体业务逻辑,将新的key,value输出。                                 4.将自定义的mapper和reduce通过job对象组装起来。                                     

自定义继承Mapper类:

package cn.itcast.hadoop.mr;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class WCMapper extends Mapper{
@Override
protected void map(LongWritable key, Text value, Mapper.Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
//接受数据v1
String line=value.toString();
//切分数据
String[] words=line.split(" ");
//循环
for(String w:words){
//出现一次,记作一个一,输出
context.write(new Text(w), new LongWritable(1));
}
}

}

自定义继承Reducer类:

package cn.itcast.hadoop.mr;

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{
@Override
protected void reduce(Text key, Iterable v2s,
Context context) throws IOException, InterruptedException {
// TODO Auto-generated method stub
//接受数据
//Text k3=k2;
//定义一个计数器
long counter=0;
//循环v2s 
for(LongWritable i :v2s){
counter+=i.get();
}
//输出
context.write(key, new LongWritable(counter ));
}
}

利用job对象将两者进行组装:

package cn.itcast.hadoop.mr;
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.FileOutputCommitter;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCont {

public static void main(String[] args) throws IOException {

//将map和reduce组装起来  构建job对象

Job job=Job.getInstance(new Configuration());

//注意:main方法所在的类
job.setJarByClass(WordCont.class);


//组装,先设置mapper的属性
job.setMapperClass(WCMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
FileInputFormat.setInputPaths(job, new Path("/words"));

//设置Reducer相关属性
job.setReducerClass(WCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
FileOutputFormat.setOutputPath(job, new Path("/wcout619"));

try {
//提交任务
job.waitForCompletion(true); //打印进度详情
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();

}

}

Hadoop序列化对象

        jar包存放在HDFS中。Client(调用jar包生成一个JobClient)给RM发送一个RPC请求,RM给Client一个jobID,一个存放jar包的路径。路径前缀+ID。这个路径就是HDFS中(默认10份)。客户端把ID和地址通过RPC的方式提交给RM,RM里面保存了这个作业的描述信息。

        RM把这个信息放在队列中,Hadoop默认调度器,FIFO。公平调度器,容量调度器。    NM小弟就通过心跳机制来获取任务。小弟跑到HDFS上获取jar包,启动相应的子进程(独立于NodeManager),Map/Reduce。

        任务:先给JobClient,持有RM进程的代理对象,接着通信,返回一个路径,由于比较固定,后面再拼接一个jobid,客户端将数据写入到HDFS中,写10份。将作业提交给RM(描述信息,jar包位置)。RM进行初始化,放入到调度器里面。6:决定起多少mapper,reducer。7:NM->RM,再起一个子进程(Map/Reduce)。

Hadoop入门3-MR介绍_第1张图片

序列化:

        把结构化对象转化为字节流。实现Seralizable接口。有个序列化ID,和反序列化ID要一致,不然出不来。

        Hadoop没有用jdk的默认序列化,因为效率太低。

Monkey m=new Monkey();    //既保存了Monkey,也保存了Animal的属性等。             反序列化之后:也保存了继承结构,信息等内容,冗余。        Serializationg->Deserilization

Hadoop序列化的特点:

        1.紧凑:高效使用存储空间  2.快读,额外开销小  3.可扩展(实现其接口就OK,两种,GOOGLE)    4.互操作,支持多语言

Hadoop的序列化格式:Writable接口。

自定义序列化对象:统计手机在某个时刻产生了多少流量。上行+下行

定义一个DataBean类,进行序列化和反序列化:

package cn.itcast.hadoop.mr.dc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;

public class DataBean implements Writable{
private String telNo;
private long upPayLoad;
private long downPayLoad;
private long totalPayLoad;


//deserialize //反序列化
//注意:顺序,类型  很关键
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
this.telNo=in.readUTF();
this.upPayLoad=in.readLong();
this.downPayLoad=in.readLong();
this.totalPayLoad=in.readLong();
}

//serialize序列化,从内存输入到流中
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
out.writeUTF(telNo);
out.writeLong(upPayLoad);
out.writeLong(downPayLoad);
out.writeLong(totalPayLoad);

}


public DataBean(){

}

public DataBean(String telNo, long upPayLoad, long downPayLoad) {
super();
this.telNo = telNo;
this.upPayLoad = upPayLoad;
this.downPayLoad = downPayLoad;
this.totalPayLoad=upPayLoad+downPayLoad;
}


@Override
public String toString() {
return this.upPayLoad+"\t"+this.downPayLoad+"\t"+this.totalPayLoad;
}
public String getTelNo() {
return telNo;
}
public void setTelNo(String telNo) {
this.telNo = telNo;
}
public long getUpPayLoad() {
return upPayLoad;
}
public void setUpPayLoad(long upPayLoad) {
this.upPayLoad = upPayLoad;
}
public long getDownPayLoad() {
return downPayLoad;
}
public void setDownPayLoad(long downPayLoad) {
this.downPayLoad = downPayLoad;
}
public long getTotalPayLoad() {
return totalPayLoad;
}
public void setTotalPayLoad(long totalPayLoad) {
this.totalPayLoad = totalPayLoad;
}

}


Main方法:包含两个静态方法,Map和Reduce:

package cn.itcast.hadoop.mr.dc;
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.io.Writable;
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;
import org.apache.zookeeper.server.DataTreeBean;


import cn.itcast.hadoop.mr.dc.DataCount.DCMapper.DCReducer;
import junit.framework.Test;


public class DataCount {

//生成一个jar包  hadoop jar /root/examples.jar cn.itcast.hadoop.mr.dc.DataCount /data.com /dataout
//运行jar包,跟着main方法的类,后面是传递进来的两个参数
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// TODO Auto-generated method stub
Configuration conf=new Configuration();
Job job=Job.getInstance(conf);
job.setJarByClass(DataCount.class);
job.setMapperClass(DCMapper.class);
//以下条件可以省略     k2 v2  and k3 v3的类型一样时可以省略
job.setMapOutputKeyClass(Test.class);
job.setMapOutputValueClass(DataBean.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));

job.setReducerClass(DCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DataTreeBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));

job.waitForCompletion(true);


}
public static class DCMapper extends Mapper{


@Override
protected void map(LongWritable key, Text value, Mapper.Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
String line=value.toString();
String[] fields=line.split("\t");
String telNo=fields[1];
long up=Long.parseLong(fields[8]);
long down=Long.parseLong(fields[9]);
DataBean bean=new DataBean(telNo,up,down);
context.write(new Text(telNo), bean);
}
public static class DCReducer extends Reducer{


@Override
protected void reduce(Text key, Iterable v2s,
Context context) throws IOException, InterruptedException {
// TODO Auto-generated method stub
long up_sum=0;
long down_sum=0;
for(DataBean bean:v2s){
up_sum+=bean.getUpPayLoad();
down_sum+=bean.getDownPayLoad();
}
DataBean bean=new DataBean("",up_sum,down_sum);
context.write(key,bean);

        }

    }

    }
    }

接着打一个jar包,在linux下输入如下命令:hadoop jar /root/examples.jar                                               cn.itcast.hadoop.mr.dc.DataCount /data.com /dataout 找到main方法的类,接着传进来两个参数就OK了。


MR提交过程:

        提交请求->RM接受数据,给客户端坐标,客户端存放jar包,反馈信息返回于RM。

如果利用debug的方式则只运行了一份,并不是在集群上面运行的。                   

Submit->connect(私有方法)->把cluster作为job的成员变量。cluster作为resouce manager的引用,就可以正常通信了。cluster里面有很多内容,RPC代理对象。方法真正被执行是在服务端,返回给了客户端。

得到存储jar包路径,得到jobId。submitClient.getNewJobID()。从代理对象得到一个JobID。可以从配置文件里面进行修改,拷贝jar包的数量,默认是10个。mapred-default.xml,默认值是10。JobsUbmitted->copyAndConfigureFiles:将一些jar包拷贝到HDFS中去。 int maps=writeSplites();这个方法决定了要起多少个map。将jobid,jar保存放地址和jar包存放信息提交给了resource manager。


Hadoop远程Debug

        JDPA:Debug调试。   JDWP:支持远程调试

-agentlib:jdwp=transport=dt_socket,address=8888,server=y,suspend=y

启动一个进程,默认就是和main方法的类一样。这些启动的都是类。NameNode,DataNode,ResourceManager等。ctrl+o:可以查看包含的内容。

修改内容:在hadoop下的etc下的env下修改内容。接着可以打开桌面的eclipse,只要进行关联了源码,就可以找到NameNode的Main方法。选择Debug As->Remote Java Application。远程JAVA应用内容->ip以及端口。




















































你可能感兴趣的:(hadoop)