google序列化協議protobuf底層源碼分析

序列化/反序列化

  • 序列化
    將對象序列化爲二進制數據【字節數組】,一般也將序列化成爲編碼,主要用於網絡傳輸、數據持久化
  • 反序列化
    將從網絡上、磁盤等讀取的字節數組還原爲原始的對象,一般也將反序列化成爲解碼,主要用於網絡傳輸對象的解碼,一邊完成遠程調用

序列化協議需要考慮什麼

  • 序列化之後碼流大的大小【佔用網絡帶寬】字節長度,這就是金錢
  • 序列化與反序列化的性能【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;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章