覺得有幫助的,請多多支持博主,點贊關注哦~
文章目錄
Hadoop序列化
1、序列化理解
1.1、序列化定義
1.1.1、概念:
序列化就是把內存中的對象,轉換成字節序列(或其他數據傳輸協議)以便於存儲(持久化)和網絡傳輸。
反序列化就是將收到字節序列(或其他數據傳輸協議)或者是硬盤的持久化數據,轉換成內存中的對象。
1.1.2、理解
1)序列化就是把內存中的對象,轉換成【字節序列】以便於存儲和網絡傳輸。
2)反序列化就是將收到【字節序列】或者是硬盤的持久化數據,轉換成對象。
書桌:
商家---》顧客(序列化的操作)
1)書桌對象->拆成了N個小零件(字節序列)
顧客收到貨:(反序列化)
2)組裝零件(字節序列)-->書桌對象
1.2、使用序列化的原因
一般來說,“活的”對象只生存在內存裏,關機斷電就沒有了。而且“活的”對象只能由本地的進程使用,不能被髮送到網絡上的另外一臺計算機。 然而序列化可以存儲“活的”對象,可以將“活的”對象發送到遠程計算機。
總而言之就是:將數據活化,並進行遠程的傳輸與存儲。
1.3、Hadoop沒使用java序列化的原因
常規java程序: mapreduce(java編寫)
1、helloworld wordcount
2、數據類型 java的常規數據類型(中間的處理程序)
+hadoop序列化類型(input參數、output參數)
- Java的序列化是一個重量級序列化框架(Serializab1e),一個對象被序列化後,會附帶很多額外的信息(各種校驗信息,header,繼承體系等),不便於在網絡中高效傳輸。
- Java序列化很強大,序列化得到的信息很詳細,但是序列化後很佔內存。
所以,hadoop 自己開發了一套序列化機制(Writable),特點如下:
- 緊湊:緊湊的格式能讓我們充分利用網絡帶寬,而帶寬是數據中心最稀缺的資
- 快速:進程通信形成了分佈式系統的骨架,所以需要儘量減少序列化和反序列化的性能開銷,這是基本的;
- 可擴展:協議爲了滿足新的需求變化,所以控制客戶端和服務器過程中,需要直接引進相應的協議,這些是新協議,原序列化方式能支持新的協議報文;
- 互操作:能支持不同語言寫的客戶端和服務端進行交互;
2、常用的Hadoop序列化類型
Java類型 | Hadoop Writable類型 |
---|---|
boolean | BooleanWritable |
byte | ByteWritable |
int | IntWritable, VIntWritable(可變長度) |
float | FloatWritable |
long | LongWritable, VLongWritable(可變長度) |
double | DoubleWritable |
String | Text |
map | MapWritable |
array | ArrayWritable |
null | NulWritable |
2.1、實現數據排序
數據排序代碼示例:
package com.biubiubiu.test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
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;
import java.io.IOException;
public class TestSort {
/*
數據:
4
5
*/
//自定義Mapper
static class CustomSortMapper extends Mapper<Object, Text,IntWritable,IntWritable>{
//爲了實現變量的重用
IntWritable number = new IntWritable();
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
System.out.println("map接收的參數:key="+key+",value="+value.toString());
int num=Integer.parseInt(value.toString());
number.set(num);
//輸出-->環形緩衝區context
context.write(number,number);
}
}
//自定義Reducer
static class CustomSortReducer extends Reducer<IntWritable,IntWritable,IntWritable,NullWritable>{
/**
*
* @param key 6
* @param values (6,6,6)
* @param context
* @throws IOException
* @throws InterruptedException
*/
IntWritable number= new IntWritable();
@Override
protected void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
StringBuffer valuesStr = new StringBuffer();
for(IntWritable value:values){
valuesStr.append(value).append(" ");
}
System.out.println("reduce端接收的參數:key="+key+",值列表是("+valuesStr.toString()+")");
//reduce輸出, 只輸出key,去重
number.set(key.get());
context.write(number,NullWritable.get());
}
}
//編寫Driver
public static void main(String[] args) {
try{
//1)創建Configuration對象,指明namespace的路徑
Configuration conf = new Configuration();
conf.set("dfs.defaultFS","hdfs://192.168.153.231:9000");
//
//2)創建Job
Job job =Job.getInstance(conf,"TestSort");
job.setJarByClass(TestSort.class);
//3)自定義Mapper進行輸出參數(key,value)的配置
job.setMapperClass(CustomSortMapper.class);
job.setMapOutputKeyClass(IntWritable.class);
job.setMapOutputValueClass(IntWritable.class);
//4)自定義Reducer進行參數的配置
job.setReducerClass(CustomSortReducer.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(NullWritable.class);
//5)配置處理的文件的路徑(input)以及處理結果存放的路徑(output)
FileInputFormat.addInputPath(job,new Path(args[0])); //data/sortnumber.txt
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//6)讓程序執行
boolean result=job.waitForCompletion(true);
if(result){
System.out.println("執行正確!!!");
}else{
System.out.println("執行失敗.....");
}
}catch(Exception ex){
System.out.println("執行出錯:"+ex.getMessage());
ex.printStackTrace();
}
}
}
修改上述代碼中的reduce,可實現不去重,全部輸出:
@Override
protected void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
StringBuffer valuesStr = new StringBuffer();
for(IntWritable value:values){
valuesStr.append(value).append(" ");
//reduce輸出 在循環中輸出值,不去重
number.set(value.get());
context.write(number, NullWritable.get());
}
System.out.println("reduce端接收的參數:key="+key+",值列表是("+valuesStr.toString()+")");
}
3、自定義序列化
3.1、實現自定義序列化注意點
自定義 bean 對象要想序列化傳輸,必須實現序列化接口,需要注意以下 7 項。
- 必須實現 Writable 接口
- 反序列化時,需要反射調用空參構造函數,所以必須有空參構造
- 重寫序列化方法
@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 {
upFlow = in.readLong();
downFlow = in.readLong();
sumFlow = in.readLong();
}
- 注意反序列化的順序和序列化的順序必須完全一致
- 要想把結果顯示在文件中,需要重寫 toString(),可用”\t”分開,方便後續用。
- 如果需要將自定義的 bean 放在 key 中傳輸,則還需要實現 comparable 接口,因爲
mapreduce 框中的 shuffle 過程一定會對 key 進行排序。
@Override
public int compareTo(FlowBean o) {
// 倒序排列,從大到小
return this.sumFlow > o.getSumFlow() ? -1 : 1;
}
3.2、整體注意
主要就是要注意上述那些。
再提一遍就是反序列化時數據讀取的順序和序列化時數據寫入順序完全一致
同時也不要忘記給各個屬性生成對應的get和set方法
4、自定義序列化類型案例
4.1、輸入數據
4.2、輸出數據
4.3、代碼
4.3.1、自定義Hadoop的序列化類型
package com.biubiubiu.pojo;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* 自定義hadoop的序列化類型
* @author biubiubiu
*/
public class Customer implements Writable {
private String phone;//電話
private String name;//名字
private double tradeMoney;//交易金額
private double backMoney;//退款金額
private double score;//評分
// private int customerId;//顧客編號
//alt + insert
public Customer() { }//必須要有,反序列化要用
public Customer(double tradeMoney, double backMoney, double score) {
this.tradeMoney = tradeMoney;
this.backMoney = backMoney;
this.score = score;
}
public Customer(String phone, String name, double tradeMoney, double backMoney, double score) {
this.phone = phone;
this.name = name;
this.tradeMoney = tradeMoney;
this.backMoney = backMoney;
this.score = score;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public double getTradeMoney() {
return tradeMoney;
}
public void setTradeMoney(double tradeMoney) {
this.tradeMoney = tradeMoney;
}
public double getBackMoney() {
return backMoney;
}
public void setBackMoney(double backMoney) {
this.backMoney = backMoney;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "CustomerBean{" +
"tradeMoney=" + tradeMoney +
", backMoney=" + backMoney +
", score=" + score +
'}';
}
/*
private double tradeMoney;//交易金額
private double backMoney;//退款金額
private double score;//評分
private String name;//名字
private int customerId;//顧客編號
*/
//序列化:對象寫入,自動調用
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeDouble(tradeMoney);
dataOutput.writeDouble(backMoney);
dataOutput.writeDouble(score);
dataOutput.writeUTF(phone);
dataOutput.writeUTF(name);
}
//反序列化:對象讀取,自動調用
@Override
public void readFields(DataInput dataInput) throws IOException {
this.tradeMoney=dataInput.readDouble();
this.backMoney=dataInput.readDouble();
this.score=dataInput.readDouble();
this.phone = dataInput.readUTF();
this.name=dataInput.readUTF();
}
//反序列化的順序要與序列化的順序一致
}
4.3.2、使用自定義序列化提取信息
package com.biubiubiu.demo;
import com.biubiubiu.pojo.Customer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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;
import java.io.IOException;
/**
* 使用自定義序列化提取信息
* @author biubiubiu
*/
public class CustomerStatistics {
//編號,手機號,姓名,交易金額,退款金額,評分,交易日期
static class CustomMapper extends Mapper<Object, Text,Text, Customer>{
Customer customer =null;
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
System.out.println("map函數的輸入值:"+key.toString()+" "+value.toString());
//1)得到當前行的內容
String line=value.toString();
if(line.startsWith("編號")){
return;
}
//2)對當前行內容進行清理
String [] words=line.split(",");
//3)向context輸出結果 (String phone, String name, double tradeMoney, double backMoney, double score)
customer=new Customer(words[1],words[2],Double.parseDouble(words[3]),
Double.parseDouble(words[4]),Integer.parseInt(words[5]));
context.write(new Text(words[1]),customer);
System.out.println("map函數的輸出結果:"+customer.toString());
}
}
static class CustomReducer extends Reducer<Text, Customer, NullWritable,Text>{
@Override
protected void setup(Context context) throws IOException, InterruptedException {
context.write(null,new Text("手機號,交易金額,退款金額,平均積分"));
}
@Override
protected void reduce(Text key, Iterable<Customer> values, Context context) throws IOException, InterruptedException {
double totalTrade=0;
double totalBack=0;
double totalScore=0;
int totalCount=0;
String phone=null;
for(Customer customer :values){
totalBack+=customer.getBackMoney(); //累加退款
totalTrade+=customer.getTradeMoney(); //累加交易金額
totalCount++;
totalScore+=customer.getScore(); //累加積分
phone=customer.getPhone();
}
//輸出
context.write(null,new Text(phone+","+totalTrade+","+totalBack+","+ String.format("%.2f",totalScore / totalCount)));
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
context.write(null,new Text("***********執行完畢***************"));
}
}
//第3部分,編寫Driver(main方法)
public static void main(String[] args) {
try{
//1)創建Configuration對象,指明namespace的路徑
Configuration conf = new Configuration();
conf.set("dfs.defaultFS","hdfs://192.168.153.231:9000");
//
//2)創建Job
Job job =Job.getInstance(conf,"customer");
job.setJarByClass(CustomerStatistics.class);
//3)自定義Mapper進行輸出參數(key,value)的配置
job.setMapperClass(CustomMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Customer.class);
//4)自定義Reducer進行參數的配置
job.setReducerClass(CustomReducer.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
//5)配置處理的文件的路徑(input)以及處理結果存放的路徑(output)
FileInputFormat.addInputPath(job,new Path("data/customer.txt"));//本地測試
FileOutputFormat.setOutputPath(job,new Path("data/1.1"));
//6)讓程序執行
boolean result=job.waitForCompletion(true);
if(result){
System.out.println("執行正確!!!");
}else{
System.out.println("執行失敗.....");
}
}catch(Exception ex){
System.out.println("執行出錯:"+ex.getMessage());
ex.printStackTrace();
}
}
}
覺得有幫助的,請多多支持博主,點贊關注哦~