Hadoop---MapReduce

MapReduce

1. 概述

     (1):MapReduce是一种分布式计算模型

     (2):有谷歌提出来的,基于GFS进行设计,主要用于搜索领域中解决海量数据的计算问题

     (3):MapReduce是由两个阶段组成:Map和Reduce,用户只需要实现map以及reduce两个函数,,既可以实现分布式计                     算,这样做的目的是简化分布式程序的开发和试用周期

 2. 组成

     (1):JobTracker/ResourceManager:任务调度者,管理多个TaskTracker。ResourceManager是Hadoop2.0版本之后引入Yarn之后用于替代JobTracke部分功能的机制

     (2):TaskTracker/NodeManager:任务执行者

3. 结构图

              

    (1):从HDFS中获取数据

    (2):MapReduce首先会将输入的数据进行逻辑切片,每一个切片是一个IInputSpit对象

    (3):每一个InputSpit对象会交给一个MapTask来执行

    (4):切片中的每一行数据都会触发一次map方法

    (5):map方法的输入的默认值默认为数据偏移量,输入这一行的数据;输出的键以及值的类型根据业务来确定

    (6):在Barrier阶段,会将所有相同的键所对应的值放入一个ArrayList中,然后产生一个迭代器交给ReduceTask来执行

    (7):在ReduceTask中,每一个键都会触发一次reduce方法

    (8):将结果写在HDFS中 

 4. 入门案例(给单词计数)

     WorldCountMapper

     读取文件

hello tom hello bob
hello joy
hello rose
hello joy
hello jerry
hello tom
hello rose
hello joy
package cn.tedu;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

//统计单词中出现的次数
public class WorldCountMapper extends Mapper<LongWritable,Text,Text, IntWritable>{
	/*
	 * MapReduce中要求传输的数据必须能够被序列化
	 * KEYIN - 输入的键的类型。默认是行的偏移量,实际上就是记录这一行在这个文件中的开始位置
	 * VALUEIN - 输入的值的类型。默认是这一行数据
	 * KEYOUT - 输出的键的类型 - 当前案例中键就是单词
	 * VALUEOUT - 输出的值的类型 - 当前案例中值是次数
	 */
	@Override
	protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
			throws IOException, InterruptedException {
		/*
		 * key值---行偏移量
		 * value--行数据
		 * context---负责将结果写出到Reduce当中
		 */
		//hello tom hello bob
		String[] arr = value.toString().split(" ");
		for (String arri : arr) {
			context.write(new Text(arri), new IntWritable(1));
		}
	}

}

WorldCountReduce

package cn.tedu;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class WorldCountReduce extends Reducer<Text, IntWritable, Text, IntWritable>{

	/*
	 * KEYIN - 输入的键的类型
	 * VALUEIN - 输入的值的类型
	 * KEYOUT - 输出的键的类型
	 * VALUEOUT - 输出的值的类型
	 */
	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,
			Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
		// 计算总次数
		/*
		 * key - 输入的键
		 * values - 存储值的迭代器
		 * context - 负责将结果写出
		 */
		int sum = 0;
	    for (IntWritable value : values) {
	    	sum+=value.get();
		}
	    context.write(key, new IntWritable(sum));
		
	}
}

      WorldCountDriver

package cn.tedu;

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.Text;

import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WorldCountDriver{
	
	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
		//在MapReduce上需要先给这个程序申请Job任务
		Configuration config = new Configuration();
		Job job = Job.getInstance(config);
		
		//设置入口类
		job.setJarByClass(WorldCountDriver.class);
		//设置mapper类
		job.setMapperClass(WorldCountMapper.class);
		//设置Reducer类
		job.setReducerClass(WorldCountReduce.class);
		
		//声明Mapper的数据结果类型
	
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		
		//声明Reducer的数据类型结果
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		
		//设置要处理的文件的路径
		FileInputFormat.addInputPath(job, new Path("hdfs://10.42.162.10:9000/txt/words.txt"));
		
		//设置输出文件路径
		FileOutputFormat.setOutputPath(job, new Path("hdfs://10.42.162.10:9000/result/worldcount"));
		
		// 启动执行
		job.waitForCompletion(true);
	}

}

  结果

bob	1
hello	9
jerry	1
joy	3
rose	2
tom	2

  补充(eclipse打包)

  项目---->右键export---->JAR file

  hadoop中运行:hadoop jar jar包的名字

 5.序列化/反序列化机制

    (1):序列化/反序列化机制

           a:在Hadoop的集群工作过程中,一般是利用RPC来进行集群节点之间的通信和消息的传输,所以要求MapReduce处理                  的对象必须可以进行序列化/反序列操作

           b:Hadoop并没有使用Java原生的序列化,而是利用的是Avro实现的序列化和反序列,并且在此基础上进行了更好的封                     装,提供了便捷的API

           c:在Hadoop中要求被序列化的对象对应的类必须实现Writable接口,重写其中的write方法以及readFields方法

           d:序列化过程中要求属性值不能为null

    (2):案例

           流量统计:

           原始数据flow.txt

    13877779999 bj zs 2145
    13766668888 sh ls 1028
    13766668888 sh ls 9987
    13877779999 bj zs 5678
    13544445555 sz ww 10577
    13877779999 sh zs 2145
    13766668888 sh ls 9987

创建一个类实现Writeable接口,就可以实现序列化AVRO机制

package cn.tedu.serialflow;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.Writable;

public class Flow implements Writable{
	
	private String ipone;
	private String city;
	private String name;
	private int Flow;
	public String getIpone() {
		return ipone;
	}
	public void setIpone(String ipone) {
		this.ipone = ipone;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getFlow() {
		return Flow;
	}
	public void setFlow(int flow) {
		Flow = flow;
	}
	@Override
	public void readFields(DataInput in) throws IOException {
		// TODO Auto-generated method stub
		this.ipone=in.readUTF();
		this.city=in.readUTF();
		this.name=in.readUTF();
		this.Flow=in.readInt();
		
	}
	@Override
	public void write(DataOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeUTF(ipone);
		out.writeUTF(city);
		out.writeUTF(name);
		out.writeInt(Flow);
		
		
	}
	

}

Mapper类

package cn.tedu.serialflow;

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.htrace.fasterxml.jackson.databind.annotation.JsonPOJOBuilder.Value;

public class FlowMapper extends Mapper<LongWritable, Text, Text, Flow> {

	public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException {
		
		String[] arr = ivalue.toString().split(" ");
		Flow f = new Flow();
		f.setIpone(arr[0]);
		f.setCity(arr[1]);
		f.setName(arr[2]);
		f.setFlow(Integer.parseInt(arr[3]));
		context.write(new Text(arr[2]), f);

	}

}

Reduce 类

package cn.tedu.serialflow;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class FlowReduce extends Reducer<Text, Flow, Text, IntWritable> {

	public void reduce(Text _key, Iterable<Flow> values, Context context) throws IOException, InterruptedException {
		// process values
		int sum = 0;
		for (Flow val : values) {
			sum += val.getFlow();
		}
		context.write(_key, new IntWritable(sum));
	}

}

Driver 类

package cn.tedu.serialflow;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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 FlowDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf, "JobName");
		job.setJarByClass(cn.tedu.serialflow.FlowDriver.class);
		// TODO: specify a mapper
		job.setMapperClass(FlowMapper.class);
		// TODO: specify a reducer
		job.setReducerClass(FlowReduce.class);

		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Flow.class);
		// TODO: specify output types
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		// TODO: specify input and output DIRECTORIES (not files)
		FileInputFormat.addInputPath(job, new Path("hdfs://10.42.162.10:9000/txt/flow.txt"));
		FileOutputFormat.setOutputPath(job, new Path("hdfs://10.42.162.10:9000/result/flowcount"));

		if (!job.waitForCompletion(true))
			return;
	}

}

数据处理结果

ls	21002
ww	10577
zs	9968

  6. 分区--Partitioner

      (1):分区的作用主要是对数据进行分类

      (2):默认只有一个分区,默认只有一个ReduceTask

      (3):每一个分区都必须对应一个ReduceTask,每一个ReduceTask都会产生一个结果文件

      (4):如果不指定,默认的类时HashPartitioner

      (5):分区号的默认值是从0开始的

      (6):举一个例子

             统计每个人,每一个月的利润----以人名分区

package cn.tedu.partprofit;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Partitioner;

public class PartProfit extends Partitioner<IntWritable, Consumer>{

	@Override
	public int getPartition(IntWritable key, Consumer consumer, int numReduceTasks) {
		
		if(consumer.getName().equals("ls")){
			return 0;
		}else if(consumer.getName().equals("zs")){
			return 1;
		}else{
			return 2;
		}	
		
	}

}

   7.排序

     (1):在MapReduce中,默认是对键会进行(自然序)升序排列的

     (2):要求作为键的对象对应的类必须实现Comparable,又因为键需要被序列化,所以一般需要实现的是WritableComparable               接口

     (3):在排序的时候,如果所有比较的字段都一样,认为是同一个键,会就会把对应的值也会分到一组,这时候就需要做一些               处理

      (4):练习:先按照月份进行升序排序,如果月份一致,则按照业绩降序排列

package cn.tedu.sortprofit;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;

public class Consumer implements WritableComparable<Consumer>{
	
	private int month;
	private String name;
	private int profit;
	
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getProfit() {
		return profit;
	}
	public void setProfit(int profit) {
		this.profit = profit;
	}
	@Override
	public void readFields(DataInput in) throws IOException {
		// TODO Auto-generated method stub
		this.month=in.readInt();
		this.name=in.readUTF();
		this.profit=in.readInt();
		
	}
	@Override
	public void write(DataOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeInt(month);
		out.writeUTF(name);
		out.writeInt(profit);
		
	}
	@Override
	public int compareTo(Consumer o) {
		// TODO Auto-generated method stub
		int i = this.month-o.month;
		if(i==0){
			int j = o.profit-this.profit;
			return j==0?-1:j;
		}
		return i;
	}
	@Override
	public String toString() {
		return "Consumer [month=" + month + ", name=" + name + ", profit=" + profit + "]";
	}
   
	

}

   8. 合并--Combiner

       (1):在MapReduce中由于MapTask数量多而ReduceTask数量少,那么ReduceTask的执行效率就成了整个MapReduce的瓶                 颈。所以可以将一部分的汇总计算任务前移到MapTask,这个时候ReduceTask的计算压力就会变小  - 这个过程称之为                 合并 - Combine
       (2):实际生产环境中,Combiner和Reducer的逻辑是一样的 - job.setCombinerClass(Reducet.class);
       (3):虽然Combiner可以提高效率,但是不是所有的场景都适合于用Combiner

     (MapReduce)举例:找出隐藏好友(A和B是好友,B和C是好友,那么A和C就是隐藏好友)

  tom rose
  tom jim
  tom smith
  tom lucy
  rose tom
  rose lucy
  rose smith
  jim tom
  jim lucy
  smith jim
  smith tom
  smith rose

          逻辑:Mapper:tom [rose,Jim,smith,lucy],如果是好友就把key:【自己--friend】,value:1

                                    假设rose和jim,Smith,Lucy是好友:key【自己-friend】,value:0

                                           假设Jim和Smith,Lucy是好友:key  【自己-friend】,value:0

                                                   。。。。。

                      以第一次的结果为源文件,只有当key对应的所有的value都是0的时候,那么自己和friend才是隐藏好友

package cn.tedu.friend;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class RelationReducer extends Reducer<Text, Text, Text, IntWritable> {

	// 在MapReduce中,这个迭代器只能遍历一次
	public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
		List<String> fs = new ArrayList<>();
		// 记录真实好友关系
		String name = key.toString();
		for (Text val : values) {
			String f = val.toString();
			fs.add(f);
			if (name.compareTo(f) < 0)
				context.write(new Text(name + "-" + f), new IntWritable(1));
			else
				context.write(new Text(f + "-" + name), new IntWritable(1));
		}
		// 推测列表中好友可能认识
		for (int i = 0; i < fs.size() - 1; i++) {
			for (int j = i + 1; j < fs.size(); j++) {
				String f1 = fs.get(i);
				String f2 = fs.get(j);
				if (f1.compareTo(f2) < 0)
					context.write(new Text(f1 + "-" + f2), new IntWritable(0));
				else 
					context.write(new Text(f2 + "-" + f1), new IntWritable(0));
					
			}
		}
	}

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言用于封装代码的单元,可以实现代码的复用和模块化。C语言定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言用于存储同类型数据的结构,可以通过索引访问和修改数组的元素。字符串是C语言用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值