day41-hadoop-mapreduce

day41-hadoop-mapreduce

hadoop-mapreduce

hadoop排序

hadoop比较使用的类和接口

WritableComparable: 支持序列化和比较的接口
WritableComparator: 比较器

hadoop如何实现排序比较

  • 自定义排序WritableComparable
bean对象作为key传输,需要实现WritableComparable接口重写CompareTo方法,就可以实现排序
  • WritableComparator
1. job.setSortComparatorClass(xxx.class); 设置key的比较器对象

2. public RawComparator getOutputKeyComparator() {
    Class<? extends RawComparator> theClass = getClass(
      JobContext.KEY_COMPARATOR, null, RawComparator.class);
      // 相关参数:mapreduce.job.output.key.comparator.class
    if (theClass != null)
        // 如果能获取到,则通过反射的方式创建比较器对象
      return ReflectionUtils.newInstance(theClass, this);
    // 如果获取不到,通过WritableComparator.get()方法来获取
    return WritableComparator.get(getMapOutputKeyClass().asSubclass(WritableComparable.class), this);
  }

3. public static WritableComparator get(
    Class<? extends WritableComparable> c, Configuration conf) {
    // 尝试从comparators中获取key的比较对象,针对于hadoop自身的序列化类型
    WritableComparator comparator = comparators.get(c);
    if (comparator == null) {
        // force the static initializers to run
        // 如果获取不到,强制类加载
        forceInit(c);
        // look to see if it is defined now
        comparator = comparators.get(c);
        // if not, use the generic one
        // 再次获取
        if (comparator == null) {
            // 如果还获取不到,就生成一个
            comparator = new WritableComparator(c, conf, true);
        }
    }
    // Newly passed Configuration objects should be used.
    ReflectionUtils.setConf(comparator, conf);
    return comparator;
}

4. WritableComparator的compare方法
public int compare(WritableComparable a, WritableComparable b) {
    return a.compareTo(b);
}    
当我们获取到key的比较器对象后,会调用compare方法进行比较,默认的compare方法的实现会调用到WritableComparable的compareTo方法

5. hadoop自身的序列化类型,都有自己的比较器对象,在类加载期间,会将类型及对应的比较器对象注册到WritableComparator中的comparators 这个map中
static {
    // register this comparator
    WritableComparator.define(Text.class, new Comparator());
}

6. 编码实现比较
    1) 为key单独编写比较器对象,xxxWritableComparator,重写compare方法实现比较规则
    2) 不为key编写比较器对象,hadoop会帮问我们创建一个比较器对象,该比较器对象会调用到WritableComparable的compareTo方法,因此我们只需要让key对象的类实现WritableComparable接口,并实现compareTo方法,定义比较规则即可

排序案例

根据之前写的流量的案例,对结果再次对总流量进行排序
分析:hadoop的比较 是针对key进行的排序

代码编辑

FlowBean

package lc.mapreduce.mapper.comparator;

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 FlowBean implements WritableComparable<FlowBean> {

    private Long upFlow;
    private Long downFlow;
    private Long sumFlow;

    public FlowBean() {
    }

    public FlowBean(Long upFlow, Long downFlow, Long sumFlow) {
        this.upFlow = upFlow;
        this.downFlow = downFlow;
        this.sumFlow = sumFlow;
    }

    @Override
    public String toString() {
        return this.upFlow + "\t" + this.downFlow + "\t" + this.sumFlow;
    }

    public Long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(Long upFlow) {
        this.upFlow = upFlow;
    }

    public Long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(Long downFlow) {
        this.downFlow = downFlow;
    }

    public Long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(Long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void setSumFlow(){
        this.sumFlow=this.downFlow+this.upFlow;
    }

    @Override
    public void write(DataOutput out) throws IOException {
        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.upFlow = in.readLong();
        this.downFlow = in.readLong();
        this.sumFlow = in.readLong();
    }

    @Override
    public int compareTo(FlowBean o) {
        return 0;
    }
}

FlowWritableComparator

package lc.mapreduce.mapper.comparator;

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

public class FlowWritableComparator extends WritableComparator {

    public FlowWritableComparator(){
        super(FlowBean.class,true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        FlowBean flowBeanA=(FlowBean) a;
        FlowBean flowBeanB=(FlowBean) b;
        // 比较总流量
        return -flowBeanA.getSumFlow().compareTo(flowBeanB.getSumFlow());
    }
}

FlowMapper

package lc.mapreduce.mapper.comparator;

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

import java.io.IOException;

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

    FlowBean outk = new FlowBean();
    Text outv = new Text();

    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, FlowBean, Text>.Context context) throws IOException, InterruptedException {

        String row = value.toString();
        String[] flows = row.split("\t");

        // 对写出的k进行赋值
        // 数据格式:3	12345678904	192.168.23.3	www.xx2.com	118	2437	200
        outk.setDownFlow(Long.parseLong(flows[flows.length - 3])); // 下线
        outk.setUpFlow(Long.parseLong(flows[flows.length - 2]));// 上线
        outk.setSumFlow();
        // 对写出的v 进行赋值
        outv.set(flows[1]);
        // 写出k v
        context.write(outk, outv);

    }
}

FlowDriver

package lc.mapreduce.mapper.comparator;

import org.apache.hadoop.conf.Configuration;
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.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class FlowDriver {
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {


        Configuration conf =new Configuration();
        Job job=Job.getInstance(conf);
        job.setJarByClass(FlowDriver.class);
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        job.setMapOutputKeyClass(FlowBean.class);
        job.setMapOutputValueClass(Text.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        FileInputFormat.setInputPaths(job,new Path("D:\\a测试文件\\input\\writableflow"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\a测试文件\\output\\writableflow4"));

        // 设置job的比较器对象
        job.setSortComparatorClass(FlowWritableComparator.class);

        job.waitForCompletion(true);
    }
}

其他

Java中的Comparable和Comparator

  • Comparable
Comparable是排序接口,若一个类实现了Comparable接口,就意味着 该类支持排序
  • Comparator
Comparator是比较器接口。创建一个实现此接口的类,此类就可以进行排序
通过 Collections.sort(list,new Comparator())
posted @ 2022-04-28 22:04  黎白昼  阅读(26)  评论(0)    收藏  举报