Hadoop系列文章索引
Hadoop入门指南之yarn介绍
排序是指按照自定义的排序规则,对数据进行排序,输出时,数据是按照排序组织的。排序往往还伴随着序列化,序列化指的是把Java对象转化成字节流来传输,到达指定位置再反序列化成Java对象,这样就提升了网络传输的速度,减轻了网络传输的压力。
序列化和反序列化在上一章分区和规约实战中已经介绍了,就是写一个StockBean的JavaBean,然后重写write和readFields方法。
我们还用上一章使用的数据:
p004,2021-01-01,5,2
p003,2021-01-01,8,1
p002,2021-01-03,3,3
p003,2021-01-03,6,2
p004,2021-01-05,9,1
p001,2021-01-05,3,2
p004,2021-01-05,2,2
p003,2021-01-07,3,1
p002,2021-01-07,6,5
p001,2021-01-08,2,1
p001,2021-01-09,6,1
这次我们不再统计库存了,而是要把库存记录按照入库数升序、出库数降序方式排序。
其实实现排序要简单的多,只要写一个JavaBean实现WritableComparable接口,重写compareTo方法,定义排序规则就好了。
这里需要输出的是库存数据,所以K2、V2是含有统计日期、入库数、出库数的StockBean和Text的商品Id,K3、V3要反过来,并且因为不需要对相同商品id的数据进行合并,Reducer里拿到数据直接写入就好了。
为什么Mapper的时候要把StockBean作为K2,是因为在Reducer前的Shuffle阶段,是按照key来做排序的,必须要把实现了WritableComparable接口的JavaBean作为Key才能使排序生效。Reducer再反过来是为了保证数据格式的不变。
首先把测试数据保存为stock,txt,接着rz -E上传到node01中,然后在hdfs上新建一个目录,把数据上传到目录中:
hdfs dfs -mkdir /stock_count3_input
hdfs dfs -put stock.txt /stock_count3_input
在IDEA新建一个package,com.demo.stock_count3。
先写一个StockBean类:
package com.demo.stock_count3;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class StockBean implements WritableComparable<StockBean> {
private String date;//统计日期
private Integer inStock;//入库数
private Integer outStock;//出库数
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public Integer getInStock() {
return inStock;
}
public void setInStock(Integer inStock) {
this.inStock = inStock;
}
public Integer getOutStock() {
return outStock;
}
public void setOutStock(Integer outStock) {
this.outStock = outStock;
}
/**
* 无参构造方法
*/
public StockBean() {
}
/**
* 有参构造方法
* @param date 统计日期
* @param inStock 入库数
* @param outStock 出库数
*/
public StockBean(String date,Integer inStock, Integer outStock) {
this.date = date;
this.inStock = inStock;
this.outStock = outStock;
}
/**
* 对象转String的方法
* @return
*/
@Override
public String toString() {
return date+"\t"+inStock+"\t"+outStock;
}
/**
* 序列化
* @param dataOutput
* @throws IOException
*/
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeUTF(date);
dataOutput.writeInt(inStock);
dataOutput.writeInt(outStock);
}
/**
* 反序列化
* @param dataInput
* @throws IOException
*/
@Override
public void readFields(DataInput dataInput) throws IOException {
date = dataInput.readUTF();
inStock = dataInput.readInt();
outStock = dataInput.readInt();
}
/**
* 排序规则,入库数升序,出库数降序
* @param o
* @return
*/
@Override
public int compareTo(StockBean o) {
int result = this.inStock-o.inStock;
if(result==0){
return o.outStock-this.outStock;
}
return result;
}
}
这里和上一篇大体上差不多,就是多了个成员变量date,重写了compareTo方法。
接着写Mapper:
package com.demo.stock_count3;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class StockMapper extends Mapper<LongWritable, Text,StockBean, Text> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String[] strings = value.toString().split(",");//对文本进行拆分
context.write(new StockBean(strings[1],Integer.parseInt(strings[2]),Integer.parseInt(strings[3])),new Text(strings[0]));
}
}
这里一定要注意K2是StockBean,而不是Text。
接着写Reducer:
package com.demo.stock_count3;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class StockReducer extends Reducer<StockBean,Text ,Text,StockBean> {
@Override
protected void reduce(StockBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text value : values) {
context.write(value,key);
}
}
}
这里只是把Key和Value倒过来写入而已。
最后是主类:
package com.demo.stock_count3;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.net.URI;
public class StockMain extends Configured implements Tool {
@Override
public int run(String[] strings) throws Exception {
Job job = Job.getInstance(super.getConf(), "stock_count3");
job.setJarByClass(StockMain.class);//指定jar的主类
job.setInputFormatClass(TextInputFormat.class);//指定输入类
TextInputFormat.addInputPath(job,new Path("hdfs://node01:8020/stock_count3_input"));//指定输入路径
job.setMapperClass(StockMapper.class);//指定Mapper类
job.setMapOutputKeyClass(StockBean.class);//指定K2
job.setMapOutputValueClass(Text.class);//指定V2
job.setReducerClass(StockReducer.class);//指定Reducer类
job.setOutputKeyClass(Text.class);//指定K3
job.setOutputValueClass(StockBean.class);//指定V3
job.setOutputFormatClass(TextOutputFormat.class);//指定输出类
Path path = new Path("hdfs://node01:8020/stock_count3_output");
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
boolean exists = fileSystem.exists(path);
if(exists){
fileSystem.delete(path,true);//如果目录存在,就先删除目录
}
TextOutputFormat.setOutputPath(job,path);//设置输出路径
boolean b = job.waitForCompletion(true);//运行job
fileSystem.close();//关闭文件系统
return b?0:1;
}
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();//新建一个配置对象
int run = ToolRunner.run(configuration, new StockMain(), args);//运行MapReduce
System.exit(run);//系统退出
}
}
写完之后打包,然后把jar包上传到node01中,之后运行:
hadoop jar original-mapreduce_demo-1.0-SNAPSHOT.jar com.demo.stock_count3.StockMain
到http://node01:50070/explorer.html#/stock_count3_output中去看输出结果:
发现确实按照入库数升序,出库数降序的方式排列了。
至此,排序的案例实战就介绍完了。
感谢观看,如果您觉得文章写得还不错,不妨点个赞。如果您觉得有什么疑惑或者不对的地方,可以留下评论,看到我会及时回复的。如果您关注一下我,那么我会更高兴的,谢谢!