序列化/反序列化
- 序列化
將對象序列化爲二進制數據【字節數組】,一般也將序列化成爲編碼,主要用於網絡傳輸、數據持久化 - 反序列化
將從網絡上、磁盤等讀取的字節數組還原爲原始的對象,一般也將反序列化成爲解碼,主要用於網絡傳輸對象的解碼,一邊完成遠程調用
序列化協議需要考慮什麼
- 序列化之後碼流大的大小【佔用網絡帶寬】字節長度,這就是金錢
- 序列化與反序列化的性能【cpu資源的佔用】
- 是否支持跨語言
Java自帶的序列化類
- ObjectInputStream:反序列化
- ObjectOutputStream:序列化
類需要實現Serializable接口,標記該類的對象是可以被序列化的
Protobuf序列化
- 格式
option java_package = "com.netty.protobuf"
option java_outer_classname = "TeacherSerializer"
message Teacher{
required int64 teacherId = 1;
required int32 age = 2;
required string name = 3;
repeated string courses = 4;
}
java_package : Teacher序列化之後的包路徑
java_outer_classname:序列化之後類的名稱
required:序列化的時候必須要設置的屬性
repeated:一個list,一個數組
1,2,3,4:序列化值的序列id,是按照這個順序來序列化的
通過提供的exe工具,來生成java可序列化的類。
Protobuf特性
- 生成的序列化器【輔助類】中保存了需要序列化的對象的類信息
java自帶的序列化類,序列化之後的字節數組會比protobuf生成的大很多,將近10倍,那是因爲它不僅保存了數據本身的信息,同時也保存了很多額外的信息,這些信息是爲了反序列化用的,記錄了類導入的包,有哪些屬性,屬性的名稱類型等,這些都保存到序列化的字節數組中。protobuf也需要這些類信息,但是它並不需要放到字節數組中,因爲根據它自動生成的序列化器,然後根據原先定義的屬性的順序和類型信息等,可以反序列化出來。 - protobuf的數據類型是動態伸縮的
例如:在內存中,int類型一定佔4個字節,但是在protobuf中,int類型是佔1-5個字節【age 120 只佔了一個字節,只動態分配了一個字節的存儲】在大量的數據分析的基礎上,一般int類型不會佔滿4個字節,有很多高位字節並用不到。long佔1~9個字節
Protobuf核心代碼解析
序列化核心代碼
public void writeRawVarint32(int value) throws IOException {
while (true) {
//0x7F:二進制0111 111,十進制爲127
//~0x7F:二進制1000 000,十進制128
//value & ~0x7F:?000 0000
//value是否小於128,小於則一個字節就可以裝下value的數據了
if ((value & ~0x7F) == 0) {
//將value的值直接轉成1個字節
writeRawByte(value);
return;
} else {
//value的值用1個字節裝不下,申請第二個字節[高位字符]
//value & 0x7F:0??? ???? 意味着第8位肯定是0,獲取到了1~7位的數據
//0x80:1000 0000
//(value & 0x7F) | 0x80:1??? ????,意味着一個字節的最高位寫成了1
//protobuf把1~7位作爲數據爲,第8位作爲符號位,第8位表示後面是否還有數據
//第8位爲1表示一個字節裝不下了,還需要再申請一個字節
writeRawByte((value & 0x7F) | 0x80);
//右移7位,再遞歸,判斷第二位的數據是否一個字節裝得下......
value >>>= 7;
}
}
}
/** Write a single byte. */
public void writeRawByte(final byte value) throws IOException {
if (position == limit) {
refreshBuffer();
}
buffer[position++] = value;
}
private void refreshBuffer() throws IOException {
if (output == null) {
// We're writing to a single buffer.
throw new OutOfSpaceException();
}
// Since we have an output stream, this is our buffer
// and buffer offset == 0
output.write(buffer, 0, position);
position = 0;
}