order001,u001,小米6,1999.9,2 order001,u001,雀巢咖啡,99.0,2 order001,u001,安慕希,250.0,2 order001,u001,经典红双喜,200.0,4 order001,u001,防水电脑包,400.0,2 order002,u002,小米手环,199.0,3 order002,u002,榴莲,15.0,10 order002,u002,苹果,4.5,20 order002,u002,肥皂,10.0,40 order003,u001,小米6,1999.9,2 order003,u001,雀巢咖啡,99.0,2 order003,u001,安慕希,250.0,2 order003,u001,经典红双喜,200.0,4 order003,u001,防水电脑包,400.0,2
问题分析网络
须要求出每个订单中成交金额最大的n笔app
本质:求分组TOPNide
实现思路:oop
map: 读取数据切分字段,封装数据到一个bean中做为key传输,key要按照成交金额比大小this
预期结果:spa
order001,u001,小米6,1999.9,3999.8 order001,u001,雀巢咖啡,99.0,198.0 order001,u001,安慕希,250.0,500.0 order001,u001,经典红双喜,200.0,800.0 order001,u001,防水电脑包,400.0,800.0 order002,u002,小米手环,199.0,597.0 order002,u002,榴莲,15.0,150.0 order002,u002,苹果,4.5,90.0 order002,u002,肥皂,10.0,400.0 order003,u001,小米6,1999.9,3999.8 order003,u001,雀巢咖啡,99.0,198.0 order003,u001,安慕希,250.0,500.0 order003,u001,经典红双喜,200.0,800.0 order003,u001,防水电脑包,400.0,800.0reduce:利用自定义GroupingComparator将数据按订单id进行分组,而后在reduce方法中输出每组数据的前N条便可.net
预期结果输出每一个订单前2条:code
order001,u001,小米6,1999.9,3999.8 order001,u001,经典红双喜,200.0,800.0 order003,u001,小米6,1999.9,3999.8 order003,u001,经典红双喜,200.0,800.0 order002,u002,小米手环,199.0,597.0 order002,u002,肥皂,10.0,400.0
代码实现orm
封装数据到一个bean做为key传输,须要实现Hadoop序列化,走网络传输对象
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 void set(String orderId, String userId, String pdtName, float price, int number) { this.orderId = orderId; this.userId = userId; this.pdtName = pdtName; this.price = price; this.number = number; this.amountFee = price * number; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getPdtName() { return pdtName; } public void setPdtName(String pdtName) { this.pdtName = pdtName; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public float getAmountFee() { return amountFee; } public void setAmountFee(float amountFee) { this.amountFee = amountFee; } @Override public String toString() { return this.orderId + "," + this.userId + "," + this.pdtName + "," + this.price + "," + this.amountFee; } public int compareTo(OrderBean o) { //比两个浮点数 return Float.compare(o.getAmountFee(),this.getAmountFee())==0 ?this.pdtName.compareTo(o.pdtName) :Float.compare(o.getAmountFee(),this.getAmountFee()); } public void write(DataOutput dataOutput) throws IOException { dataOutput.writeUTF(this.orderId); dataOutput.writeUTF(this.userId); dataOutput.writeUTF(this.pdtName); dataOutput.writeFloat(this.price); dataOutput.writeInt(this.number); } public void readFields(DataInput dataInput) throws IOException { this.orderId=dataInput.readUTF(); this.userId=dataInput.readUTF(); this.pdtName=dataInput.readUTF(); this.price=dataInput.readFloat(); this.number=dataInput.readInt(); this.amountFee=this.price * this.number; } }
实现逻辑代码
public class OrderTopn { public static class OrderTopnMapper extends Mapper<LongWritable,Text,Text,OrderBean>{ OrderBean orderBean = new OrderBean(); Text k=new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); orderBean.set(fields[0],fields[1], fields[2],Float.parseFloat(fields[3]), Integer.parseInt(fields[4])); k.set(fields[0]); //序列化的数据跟原来的不同了 context.write(k,orderBean); } } public static class OrderTopnReducer extends Reducer<Text,OrderBean,OrderBean,NullWritable>{ @Override protected void reduce(Text key, Iterable<OrderBean> values, Context context) throws IOException, InterruptedException { int topn = context.getConfiguration().getInt("order.top.n", 3); ArrayList<OrderBean> beanList = new ArrayList<OrderBean>(); //reduce task提供的values迭代器,每次迭代返回的是同一个对象,只是set了不一样的值 for(OrderBean orderBean:values){ //构造一个新的对象,来存储本次迭代出来的值 OrderBean newBean = new OrderBean(); newBean.set(orderBean.getOrderId(),orderBean.getUserId(),orderBean.getPdtName() ,orderBean.getPrice(),orderBean.getNumber()); beanList.add(newBean); } //对beanList中的orderBean对象进行排序(金额,商品名称) Collections.sort(beanList); for(int i=0;i<topn;i++){ context.write(beanList.get(i),NullWritable.get()); } } } public static void main(String[] args) throws Exception{ Configuration conf = new Configuration(); conf.setInt("order.top.n",2); Job job = Job.getInstance(conf); //动态获取jar包在哪里 job.setJarByClass(OrderTopn.class); //2.封装参数:本次job所要调用的mapper实现类 job.setMapperClass(OrderTopnMapper.class); job.setReducerClass(OrderTopnReducer.class); //3.封装参数:本次job的Mapper实现类产生的数据key,value的类型 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(OrderBean.class); //4.封装参数:本次Reduce返回的key,value数据类型 job.setOutputKeyClass(OrderBean.class); job.setOutputValueClass(NullWritable.class); //6.封装参数:想要启动的reduce task的数量 job.setNumReduceTasks(2); FileInputFormat.setInputPaths(job,new Path("F:\\mrdata\\ordertopn\\input")); FileOutputFormat.setOutputPath(job,new Path("F:\\mrdata\\ordertopn\\out")); boolean res = job.waitForCompletion(true); System.exit(res ? 0:-1); } }
输出结果
part-r-000000
order001,u001,小米6,1999.9,3999.8
order001,u001,经典红双喜,200.0,800.0
order003,u001,小米6,1999.9,3999.8
order003,u001,经典红双喜,200.0,800.0
part-r-000001
order002,u002,小米手环,199.0,597.0
order002,u002,肥皂,10.0,400.0
原理图
代码实现
OrderBean
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 void set(String orderId, String userId, String pdtName, float price, int number) { this.orderId = orderId; this.userId = userId; this.pdtName = pdtName; this.price = price; this.number = number; this.amountFee = price * number; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getPdtName() { return pdtName; } public void setPdtName(String pdtName) { this.pdtName = pdtName; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public float getAmountFee() { return amountFee; } public void setAmountFee(float amountFee) { this.amountFee = amountFee; } @Override public String toString() { return this.orderId + "," + this.userId + "," + this.pdtName + "," + this.price + "," + this.amountFee; } public int compareTo(OrderBean o) { return this.orderId.compareTo(o.getOrderId())==0? Float.compare(o.getAmountFee(),this.getAmountFee()) :this.orderId.compareTo(o.getOrderId()); } public void write(DataOutput dataOutput) throws IOException { dataOutput.writeUTF(this.orderId); dataOutput.writeUTF(this.userId); dataOutput.writeUTF(this.pdtName); dataOutput.writeFloat(this.price); dataOutput.writeInt(this.number); } public void readFields(DataInput dataInput) throws IOException { this.orderId=dataInput.readUTF(); this.userId=dataInput.readUTF(); this.pdtName=dataInput.readUTF(); this.price=dataInput.readFloat(); this.number=dataInput.readInt(); this.amountFee=this.price * this.number; } }
OrderIdPartitioner :重写分发规则
public class OrderIdPartitioner extends Partitioner<OrderBean,NullWritable> { public int getPartition(OrderBean key, NullWritable value, int numPartitions) { //按照订单中的orderId来分发数据 return (key.getOrderId().hashCode() & Integer.MAX_VALUE)% numPartitions; } }
OrderIdGroupComparator :重写reduce比较排序规则
public class OrderIdGroupComparator extends WritableComparator { public OrderIdGroupComparator() { //调用父类构造器 super(OrderBean.class,true); } @Override public int compare(WritableComparable a, WritableComparable b) { OrderBean o1=(OrderBean)a; OrderBean o2=(OrderBean)b; //orderId相同返回0-》为一组 return o1.getOrderId().compareTo(o2.getOrderId()); } }
OrderTopn :程序入口
public class OrderTopn { public static class OrderTopenMapper extends Mapper<LongWritable,Text,OrderBean,NullWritable>{ OrderBean orderBean=new OrderBean(); NullWritable v=NullWritable.get(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); orderBean.set(fields[0],fields[1], fields[2],Float.parseFloat(fields[3]), Integer.parseInt(fields[4])); context.write(orderBean,v); } } public static class OrderTopnReducer extends Reducer<OrderBean,NullWritable,OrderBean,NullWritable>{ /** * 虽然reduce方法中的参数key只有一个,但只要迭代器迭代一次, * key中的值就会变 */ @Override protected void reduce(OrderBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException { int i=0; for(NullWritable v:values){ context.write(key,v); if(++i==3){ return; } } } } public static void main(String[] args) throws Exception{ Configuration conf = new Configuration(); Job job = Job.getInstance(conf); //动态获取jar包在哪里 job.setJarByClass(OrderTopn.class); //2.封装参数:本次job所要调用的mapper实现类 job.setMapperClass(OrderTopenMapper.class); job.setReducerClass(OrderTopnReducer.class); //3.封装参数:本次job的Mapper实现类产生的数据key,value的类型 job.setMapOutputKeyClass(OrderBean.class); job.setMapOutputValueClass(NullWritable.class); //4.封装参数:本次Reduce返回的key,value数据类型 job.setOutputKeyClass(OrderBean.class); job.setOutputValueClass(NullWritable.class); job.setPartitionerClass(OrderIdPartitioner.class); job.setGroupingComparatorClass(OrderIdGroupComparator.class); //6.封装参数:想要启动的reduce task的数量 job.setNumReduceTasks(2); FileInputFormat.setInputPaths(job,new Path("F:\\mrdata\\ordertopn\\input")); FileOutputFormat.setOutputPath(job,new Path("F:\\mrdata\\ordertopn\\out-2")); boolean res = job.waitForCompletion(true); System.exit(res ? 0:-1); } }
结果输出
part-r-000000
order002,u002,小米手环,199.0,597.0
order002,u002,肥皂,10.0,400.0
order002,u002,榴莲,15.0,150.0
part-r-000001
order001,u001,小米6,1999.9,3999.8 order001,u001,防水电脑包,400.0,800.0 order001,u001,经典红双喜,200.0,800.0 order003,u001,小米6,1999.9,3999.8 order003,u001,经典红双喜,200.0,800.0 order003,u001,防水电脑包,400.0,800.0