MapReduce模型与Yarn

MapReduce模型

MapReduce编程遵循一个特定的流程,首先编写map函数和reduce函数,最好使用单元测试来确保函数的运行符合预期,然后编写一个驱动程序来运行作业,看这个作业是否可以正确运行。当然你也可以使用本地IDE来调试用例,使其能正确处理输入,并得到预期输出。

map函数

要编写map函数,首先需要继承Hadoop提供的Mapper这个类(org.apache.hadoop.mapreduce.Mapper),然后在map中编写自己的业务逻辑。例如下面这段代码,Wordcount的map部分。

public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] words = line.split(" ");
        for(String word:words){
            context.write(new Text(word),new IntWritable(1));
        }
    }
}

需要解释的是map(LongWritable key, Text value, Context context)函数的三个参数,第一个是偏移量,这个与数据划分有关,第二个参数,MapReduce默认使用的是TextInputFormat输入格式,即按行读取,context则是上下文参数,将map的输出交给这个上下文参数。 

因为mapper这个类使用了泛型,而且其中的参数类型都应该是实现了HADOOP序列化框架的类型,而不是java提供的基本数据类型,装换规则如下:

# String比较例外,Hadoop中与之对应的是Text,注意package应该是org.apache.hadoop.io.
String->Text

#其余类型则在后面加上Writable即可
int->IntWritable
Long->LongWritable

除此之外,你还可以指定自定义类型,但是有一些要求,首先需要实现WritableComparable接口,其次指明序列化和反序列化的规则。假设我有一个OderBean,则代码可以这样:其中的write就是序列化,readFields就是反序列化。

public class OrderBean implements WritableComparable<OrderBean>{

	private String orderId;
	private String userId;
	private String pdtName;
	private float price;
	private int number;
	private float amountFee;

        // 需要有无参构造方法,不然无法初始化
        public FlowBean(){}

        //省略了setter和getter方法

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(this.orderId);
		out.writeUTF(this.userId);
		out.writeUTF(this.pdtName);
		out.writeFloat(this.price);
		out.writeInt(this.number);

	}

	@Override
	public void readFields(DataInput in) throws IOException {
		this.orderId = in.readUTF();
		this.userId = in.readUTF();
		this.pdtName = in.readUTF();
		this.price = in.readFloat();
		this.number = in.readInt();
		this.amountFee = this.price * this.number;
	}


}

map阶段的到是许多的key-value这样的键值对。

补充一点:map阶段的结果是给reduce的,在reduce阶段我们可以指定输出为几个文件,通过setNumReduceTasks可以指定开启多少个reduce task。这里面就涉及到map的数据按照什么规则划分给reduce task。Mapper任务划分数据的过程就称作Partition。负责实现划分数据的类称作Partitioner。Hadoop内置了分区类,称作HashPartitioner,源码如下:

public class HashPartitioner<K2, V2> implements Partitioner<K2, V2> {
    public HashPartitioner() {
    }

    public void configure(JobConf job) {
    }

    public int getPartition(K2 key, V2 value, int numReduceTasks) {
        return (key.hashCode() & 2147483647) % numReduceTasks;
    }
}

 参见源码可知,划分数据时默认按照key的hash码对numReduceTasks求余数。至于它与(&)上一个极大的数,是防止key的hash码如果很大(超过int的范围),那就会溢出。如果我们想改变划分数据的规则,可以实现自己的Partitioner,例如下面代码

public class OrderIdPartitioner extends Partitioner<OrderBean, NullWritable>{

	@Override
	public int getPartition(OrderBean key, NullWritable value, int numPartitions) {
		// 按照订单中的orderid来分发数据
		return (key.getOrderId().hashCode() & Integer.MAX_VALUE) % numPartitions;
	}

}

 实现自己的Partitioner之后,需要在驱动程序中指定使用的是自定义Partitioner。

job.setPartitionerClass(OrderIdPartitioner.class);

reduce函数

与map类似,也需要继承Reducer这个类,如下代码是Wordcount的reduce部分

public class WordcountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int count=0;
        Iterator<IntWritable> iterator = values.iterator();
        while (iterator.hasNext()){
            IntWritable value = iterator.next();
            count+=value.get();
        }
        context.write(key,new IntWritable(count));
    }
}

reduce函数传入的参数中是map阶段Key-Value,并且将相同key的许多value封装到聚合在一起,即reduce函数的values参数可以得到相同Key的多个值,它是一个可迭代对象,并且支持增强for循环。

补充一点:reduce函数会将key相同的聚合在一起,我们怎么判两个key相等呢,例如有些情况下,我们的map函数输出的key不是Hadoop内置的类型,而是自定义的类型,如OderBean,如果我们只需要OderId变量相同的map输出就被规约到一起,如果不自定义分组方式,那么只有所有属性都相等的情况才会被规约到一起。这种情况下,我们就需要使用GroupingComparatorClass来自定义分组方式。我们需要定义一个Comparator函数,令其继承WritableComparator,并重写compare方法。在compare方法方法中,我们定义规约器的key分组方式。通过这种方式,我们就可以将oderId相同的分为同一个组。代码如下:

public class OrderIdGroupingComparator extends WritableComparator{
	
	public OrderIdGroupingComparator() {
		super(OrderBean.class,true);
	}
	
	@Override
	public int compare(WritableComparable a, WritableComparable b) {
		
		OrderBean o1 = (OrderBean) a;
		OrderBean o2 = (OrderBean) b;
		
		return o1.getOrderId().compareTo(o2.getOrderId());
	}
}

JobSubmit函数

我们编写好mapper和reducer函数之后,就可以编写驱动程序将MapReduce程序提交运行,驱动程序如下(提交到yarn运行,yarn上需要有相应配置,具体见下面yarn部分):

public class JobSubmit {
    public static void main(String[] args) throws  Exception{
        
        // 如果有特别的设置,可通过conf配置。
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        // 1.设置主方法类
        job.setJarByClass(JopSubmitter.class);

        // 2、封装参数: 本次job所要调用的Mapper实现类、Reducer实现类
        job.setMapperClass(WordcountMapper.class);
        job.setReducerClass(WordcountReducer.class);

        // 3、封装参数:本次job的Mapper实现类、Reducer实现类产生的结果数据的key、value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // 4、封装参数:本次job要处理的输入数据集所在路径、最终结果的输出路径
        FileInputFormat.setInputPaths(job, new Path("/wordcount/input"));
        // 注意:输出路径必须不存在
        FileOutputFormat.setOutputPath(job, new Path("/wordcount/output"));  

        // 5、封装参数:想要启动的reduce task的数量
        job.setNumReduceTasks(2);

        // 6、提交job给yarn
        boolean res = job.waitForCompletion(true);
        // 设置状态码看是否完成
        System.exit(res?0:-1);
    }
}

如果你只需要验证MapReduce是否符合预期,并且只用少量数据测试,可以在IDE环境下运行,当然需要配置一些本地环境,具体见另外一篇博客:https://blog.csdn.net/wangshun_410/article/details/90681225

在IDE中运行,只需要更改输入路径和输出路径即可

// 本地磁盘读取
FileInputFormat.setInputPaths(job, new Path("F:\\mrdata\\wordcount\\input"));
// 注意:输出路径必须不存在
FileOutputFormat.setOutputPath(job,new Path("F:\\mrdata\\wordcount\\output"));

 

Yarn快速理解

yarn的基本概念

yarn是一个分布式程序的运行调度平台

yarn中有两大核心角色

Resource Manager

接受用户提交的分布式计算程序,并为其划分资源

管理、监控各个Node Manager上的资源情况,以便于均衡负载

Node Manager

管理它所在机器的运算资源(cpu + 内存)

负责接受Resource Manager分配的任务,创建容器、回收资源

YARN的安装

node manager在物理上应该跟data node部署在一起

resource manager在物理上应该独立部署在一台专门的机器上

1、修改配置文件:

vi yarn-site.xml

<configuration>

<property>

<name>yarn.resourcemanager.hostname</name>

<value>node1</value>

</property>



<property>

<name>yarn.nodemanager.aux-services</name>

<value>mapreduce_shuffle</value>

</property>
  1. scp这个yarn-site.xml到其他节点
  2. 启动yarn集群:start-yarn.sh  (注:该命令应该在resourcemanager所在的机器上执行)
  3. 用jps检查yarn的进程,用web浏览器查看yarn的web控制台,网址http://node1:8088

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值