MapReduce深入操作

一、自定義數據類型
在設計的系統開發過程之中,有可能要參與分析的文件會有很多,並且文件的組成結構也可能會非常的複雜,所以來講在整個的Hadoop裏面可以針對於用戶的需求實現自定義類型。 現在假如說有以下一個信息文件,文件的組成格式(購物統計):

用戶名[0]、省份[1]、城市[2]、購買日期時間[3]、商品名稱[4]、商品分類[5]、商品子分類[6]、商品價格[7]、商品購買價格[8]

希望可以通過一種數據類型能夠描述出以下幾種關係:
1、保留有省份的花銷(商品原始價格、成交價格、折扣的價格);
2、保留有用戶的花銷(商品原始價格、成交價格、折扣的價格);
3、保留有商品分類的花銷(商品原始價格、成交價格、折扣的價格);


在整個的MapReduce之中並不會提供有任何的一種類型來描述這種花銷的結構,那麼這樣的話就需要定義一個自己的數據類型,而所有的數據類型一定要實現一個接口:org.apache.hadoop.io.Writable;

/**
* 實現了自定義的記錄數據價格的結構
* @author mldn
*/
public class RecordWritable implements Writable {
    private double price ; // 商品的原始價格
    private double deal ; // 商品的成交價格
    private double discount ; // 商品的折扣價格
    public RecordWritable() {
        // RecordWritable類需要在讀取的時候執行數據反序列化操作;
    }
    // RecordWritable類要在數據創建的時候接收內容
    public RecordWritable(double price, double deal, double discount) {
        super();
        this.price = price;
        this.deal = deal;
        this.discount = discount;
    }
    @Override
    public void write(DataOutput output) throws IOException {
        output.writeDouble(this.price);
        output.writeDouble(this.deal);
        output.writeDouble(this.discount);
    }
    @Override
    public void readFields(DataInput input) throws IOException {
        this.price = input.readDouble() ;
        this.deal = input.readDouble() ;
        this.discount = input.readDouble() ;
    }
    @Override
    public String toString() {
        return "RecordWritable [price=" + price + ", deal=" + deal + ", discount=" + discount + "]";
    }
    public double getPrice() {
        return price;
    }
    public double getDeal() {
        return deal;
    }
    public double getDiscount() {
        return discount;
    }
}

最終的結果一定要對數據進行MapReduce 操作統計,所以在整個的統計裏面,現在希望可以實現這樣的操作統計:
· 可以實現根據省份數據得到的統計結果;
定義Map 數據處理

/**
* 針對於每一行發送的數據進行拆分處理,將每一行的數據拆分爲key與value的形式<br>
* 輸入類型:關注的是value的內容;<br>
* 輸出類型:單詞名稱=數量(1)<br>
* @author mldn
*/
private static class RecordMapper extends Mapper<Object, Text, Text, RecordWritable> {
    @Override
    protected void map(Object key, Text value, Mapper<Object, Text, Text, RecordWritable>.Context context) throws       IOException, InterruptedException {
        // 讀取每一行的數據,Map是根據換行作爲讀取的分割符;
        String str = value.toString(); // 取的每一行的內容
        // 所有的單詞要求按照空格進行拆分
        String result[] = str.split(","); // 按照“,”拆分
        // 以後如果要根據不同的方式來進行統計,則此處可以取得不同的內容
        String province = result[1] ; // 取得省份的信息內容
        double price = Double.parseDouble(result[7]) ; // 取得原始價格
        double deal = Double.parseDouble(result[8]) ; // 成交價格
        RecordWritable rw = new RecordWritable(price, deal, price-deal) ;
        context.write(new Text(province), rw); // 將數據取出
    }
}

定義Reduce 處理類

private static class RecordReducer extends Reducer<Text, RecordWritable, Text, RecordWritable> {
    @Override
    protected void reduce(Text key, Iterable<RecordWritable> values,Reducer<Text, RecordWritable, Text, RecordWritable>.Context context)
        // 計算出消費的總價格數據
        double priceSum = 0.0 ;
        double dealSum = 0.0 ;
        double discountSum = 0.0 ;
        for (RecordWritable rw : values) {
            priceSum += rw.getPrice() ;
            dealSum += rw.getDeal() ;
            discountSum += rw.getDiscount() ;
        }
        RecordWritable rw = new RecordWritable(priceSum, dealSum, discountSum) ;
        context.write(key, rw);
    }
}

定義相關的作業進行處理

public class Record {
    private static final String OUTPUT_PATH = "hdfs://192.168.122.132:9000/output-" ;
    // 具體的輸入的路徑由用戶自己來輸入,而我們來定義一個屬於自己的輸出路徑,格式“output-20201010”
    public static void main(String[] args) throws Exception {
        if (args.length != 1) { // 現在輸入的參數個數不足,那麼則應該退出程序
        System.out.println("本程序的執行需要兩個參數:HDFS輸入路徑");
        System.exit(1); // 程序退出
        }
        Configuration conf = new Configuration() ; // 此時需要進行HDFS操作
        String paths [] = new GenericOptionsParser(conf,args).getRemainingArgs() ; //將輸入的兩個路徑解析爲HDFS的路徑
        // 需要定義一個描述作業的操作類
        Job job = Job.getInstance(conf, "hadoop") ;
        job.setJarByClass(Record.class); // 定義本次作業執行的類的名稱
        job.setMapperClass(RecordMapper.class); // 定義本次作業完成所需要的Mapper程序類
        job.setMapOutputKeyClass(Text.class); // 定義Map輸出數據的Key的類型
        job.setMapOutputValueClass(RecordWritable.class); // 定義Map輸出數據的Value的類型
        job.setReducerClass(RecordReducer.class); // 定義要使用的Reducer程序處理類
        job.setOutputKeyClass(Text.class); // 最終輸出統計結果的key的類型
        job.setOutputValueClass(RecordWritable.class); // 最終輸出統計結果的value的類型
        // 所有的數據都在HDFS上進行操作,那麼就必須使用HDFS提供的程序進行數據的輸入配置
        FileInputFormat.addInputPath(job, new Path(paths[0])); // 定義輸入的HDFS路徑
        // 輸出路徑可以自己進行定義
        FileOutputFormat.setOutputPath(job,
        new Path(OUTPUT_PATH + new SimpleDateFormat("yyyyMMdd").format(new
        Date())));// 定義統計結果的輸出路徑
        System.exit(job.waitForCompletion(true) ? 0 : 1); // 程序執行完畢後要進行退出
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章