成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解RPC框架的序列化方案

開發 架構
json需要內存去解析能理解,但為什么json序列化還需要磁盤開銷啊。json序列化的二進制數據在體量比其他序列化方法小一些吧,可以減少帶寬和流量?

1 為什么需要序列化?

網絡傳輸的數據須是二進制數據,但調用方請求的出入參數都是對象:

  • ? 對象不能直接在網絡傳輸,需提前轉成可傳輸的二進制,且要求可逆,即“序列化”將對象轉換成二進制數據
  • ? 這時,服務提供方就能正確從二進制數據中分割出不同請求,同時根據請求類型和序列化類型,把二進制的消息體逆向還原成請求對象,即“反序列化”將二進制轉換為對象

圖片

序列化與反序列化

RPC框架為何需要序列化?

回想RPC通信流程:

圖片

RPC通信流程圖

2 序列化方式

2.1 JDK原生序列化

案例:

import java.io.*;

public class Student implements Serializable {
//學號
private int no;
//姓名
private String name;

public int getNo() {
return no;
}

public void setNo(int no) {
this.no = no;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
String home = System.getProperty("user.home");
String basePath = home + "/Desktop";
FileOutputStream fos = new FileOutputStream(basePath + "student.dat");
Student student = new Student();
student.setNo(100);
student.setName("TEST_STUDENT");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(student);
oos.flush();
oos.close();

FileInputStream fis = new FileInputStream(basePath + "student.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Student deStudent = (Student) ois.readObject();
ois.close();

System.out.println(deStudent);

}
}
  • ? 序列化具體由ObjectOutputStream完成
  • ? 反序列化的具體實現是由ObjectInputStream完成

JDK序列化過程:

圖片

ObjectOutputStream序列化過程圖

序列化過程就是在讀取對象數據的時候,不斷加入一些特殊分隔符,這些特殊分隔符用于在反序列化過程中截斷用。

  • ? 頭部數據,聲明序列化協議、序列化版本,用于高低版本向后兼容
  • ? 對象數據主要包括類名、簽名、屬性名、屬性類型及屬性值,當然還有開頭結尾等數據,除了屬性值屬于真正的對象值,其他都是為了反序列化用的元數據
  • ? 存在對象引用、繼承的情況下,就是遞歸遍歷“寫對象”邏輯

將對象的類型、屬性類型、屬性值按固定格式寫到二進制字節流中來完成序列化,再按固定格式讀出對象的類型、屬性類型、屬性值,通過這些信息重建一個新的對象,完成反序列化。

2.2 JSON

典型KV方式,沒有數據類型,是一種文本型序列化框架。

  • ? JSON進行序列化的額外空間開銷較大
  • ? JSON沒有類型,但像Java這種強類型語言,需通過反射統一解決,性能不太好

所以如果RPC框架選用JSON序列化,服務提供者與服務調用者之間傳輸的數據量要相對較小。

2.3 Hessian

動態類型、二進制、緊湊的,并且可跨語言移植的一種序列化框架。比JDK、JSON更加緊湊,性能上要比JDK、JSON序列化高效很多,而且生成的字節數更小。

使用代碼示例如下:

Student student = new Student();
student.setNo(101);
student.setName("HESSIAN");

//把student對象轉化為byte數組
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(bos);
output.writeObject(student);
output.flushBuffer();
byte[] data = bos.toByteArray();
bos.close();

//把剛才序列化出來的byte數組轉化為student對象
ByteArrayInputStream bis = new ByteArrayInputStream(data);
Hessian2Input input = new Hessian2Input(bis);
Student deStudent = (Student) input.readObject();
input.close();

System.out.println(deStudent);

相對于JDK、JSON,由于Hessian更加高效,生成的字節數更小,有非常好的兼容性和穩定性,所以Hessian更加適合作為RPC框架遠程通信的序列化協議。

但Hessian本身也有問題,官方版本對Java里面一些常見對象的類型不支持,比如:

  •  Linked系列,LinkedHashMap、LinkedHashSet等,但是可以通過擴展CollectionDeserializer類修復
  •  Locale類,可以通過擴展ContextSerializerFactory類修復
  • Byte/Short反序列化的時候變成Integer

2.4 Protobuf

Protobuf 是 Google 公司內部的混合語言數據標準,是一種輕便、高效的結構化數據存儲格式,可以用于結構化數據序列化,支持Java、Python、C++、Go等語言。Protobuf使用的時候需要定義IDL(Interface description language),然后使用不同語言的IDL編譯器,生成序列化工具類,它的優點是:

  •  序列化后體積相比 JSON、Hessian小很多;
  • IDL能清晰地描述語義,所以足以幫助并保證應用程序之間的類型不會丟失,無需類似 XML 解析器;
  • 序列化反序列化速度很快,不需要通過反射獲取類型;
  • 消息格式升級和兼容性不錯,可以做到向后兼容。

使用代碼示例如下:

/**
*
* // IDl 文件格式
* synax = "proto3";
* option java_package = "com.test";
* option java_outer_classname = "StudentProtobuf";
*
* message StudentMsg {
* //序號
* int32 no = 1;
* //姓名
* string name = 2;
* }
*
*/

StudentProtobuf.StudentMsg.Builder builder = StudentProtobuf.StudentMsg.newBuilder();
builder.setNo(103);
builder.setName("protobuf");

//把student對象轉化為byte數組
StudentProtobuf.StudentMsg msg = builder.build();
byte[] data = msg.toByteArray();

//把剛才序列化出來的byte數組轉化為student對象
StudentProtobuf.StudentMsg deStudent = StudentProtobuf.StudentMsg.parseFrom(data);

System.out.println(deStudent);

Protobuf 非常高效,但是對于具有反射和動態能力的語言來說,這樣用起來很費勁,這一點就不如Hessian,比如用Java的話,這個預編譯過程不是必須的,可以考慮使用Protostuff。

Protostuff不需要依賴IDL文件,可以直接對Java領域對象進行反/序列化操作,在效率上跟Protobuf差不多,生成的二進制格式和Protobuf是完全相同的,可以說是一個Java版本的Protobuf序列化框架。但在使用過程中,我遇到過一些不支持的情況,也同步給你:

  • ? 不支持null;
  • ? ProtoStuff不支持單純的Map、List集合對象,需要包在對象里面。

3 RPC序列化選型

3.1 性能和效率

3.2 空間開銷

即序列化之后的二進制數據的體積大小。序列化后的字節數據體積越小,網絡傳輸的數據量就越小,傳輸數據的速度也就越快,由于RPC是遠程調用,那么網絡傳輸的速度將直接關系到請求響應的耗時。

3.3 通用性和兼容性

某類型為集合類的入參服務調用者不能解析了,服務提供方將入參類加一個屬性之后服務調用方不能正常調用,升級了RPC版本后發起調用時報序列化異常…

通用性和兼容性的優先級考慮很高,直接關系到服務調用穩定性和可用率。看重這種序列化協議在版本升級后的兼容性,是否支持更多的對象類型,是否跨平臺、跨語言,是否有很多人已用過并踩過很多坑,其次考慮性能、效率和空間開銷。

3.4 安全性

JDK原生序列化存在漏洞。如果序列化存在安全漏洞,線上服務可能被入侵:

圖片

首選Hessian與Protobuf,性能、時間開銷、空間開銷、通用性、兼容性和安全性上,都滿足要求:

  • ? Hessian使用更方便,在對象的兼容性上更好
  • ? Protobuf則更加高效,更通用

4 FAQ

4.1 對象構造得太復雜

屬性很多,并且存在多層的嵌套,比如A對象關聯B對象,B對象又聚合C對象,C對象又關聯聚合很多其他對象,對象依賴關系過于復雜。

序列化框架在序列化與反序列化對象時,對象越復雜就越浪費性能,消耗CPU,這會嚴重影響RPC框架整體的性能。

4.2 對象太龐大

RPC請求經常超時,排查后發現他們的入參對象非常得大,比如為一個大List或者大Map,序列化之后字節長度達到了上兆字節。這種情況同樣會嚴重地浪費性能、CPU,并且序列化一個如此大的對象是很耗費時間的,這肯定會直接影響到請求耗時。

4.3 使用序列化框架不支持的類作為入參類

如Hessian天然不支持LinkHashMap、LinkedHashSet等,而且大多數情況下最好不要使用第三方集合類,如Guava中的集合類,很多開源的序列化框架都是優先支持編程語言原生的對象。因此如果入參是集合類,應盡量選用原生的、最為常用的集合類,如HashMap、ArrayList。

4.4 對象有復雜繼承關系

序列化對象時會將對象屬性一一序列化,當有繼承關系時,會不停尋找父類,遍歷屬性。就像問題1,對象關系越復雜,越浪費性能。

在RPC框架的使用過程中,盡量構建簡單的對象作為入參和返回值對象,避免上述問題。

5 總結

使用RPC框架的過程中,我們構造入參、返回值對象,主要記住以下幾點:

  1. 1. 對象要盡量簡單,沒有太多的依賴關系,屬性不要太多,盡量高內聚;
  2. 2. 入參對象與返回值對象體積不要太大,更不要傳太大的集合;
  3. 3. 盡量使用簡單的、常用的、開發語言原生的對象,尤其是集合類;
  4. 4. 對象不要有復雜的繼承關系,最好不要有父子類的情況。

實際上,雖然RPC框架可以讓我們發起遠程調用就像調用本地一樣,但在RPC框架的傳輸過程中,入參與返回值的根本作用就是用來傳遞信息的,為了提高RPC調用整體的性能和穩定性,我們的入參與返回值對象要構造得盡量簡單。

6 FAQ

RPC框架在序列化框架的選型上,你認為還需要考慮哪些因素?你還知道哪些優秀的序列化框架,它們又是否適合在RPC調用中使用?

序列化一般用在協議里面的payload里。

Redis使用的RESP,在做序列化時也是會增加很多冗余的字符,但它勝在實現簡單、可讀性強易于理解。

JSON和XML使用字符串表示所有的數據,對于非字符數據來說,字面量表達會占用很多額外的存儲空間,并且會嚴重受到數值大小和精度的影響。一個32位浮點數 1234.5678 在內存中占用 4 bytes 空間,如果存儲為 utf8 ,則需要占用 9 bytes空間,在JS這樣使用utf16表達字符串的環境中,需要占用 18 bytes空間。使用正則表達式進行數據解析,在面對非字符數據時顯得十分低效,不僅要耗費大量的運算解析數據結構,還要將字面量轉換成對應的數據類型。

在面對海量數據時,這種格式本身就能夠成為整個系統的IO與計算瓶頸,甚至直接overflow。

常見的序列化協議有:xml json protobuf jdk等 xml和json可讀性好,序列化后空間大,性能差,而且json序列化后無類型,需要反射獲取對象類型。而protobuf則是可讀性差點,序列化后占用空間小,性能好,不需要反序列化獲取屬性類型等優點。對性能要求高的原則protobuf比較好點

為什么JSON的額外開銷大呢?是因為存在大量的換行嗎

最明顯的就是你說的數據包大,因為字符相對二進制更占空間。

json需要內存去解析能理解,但為什么json序列化還需要磁盤開銷啊。json序列化的二進制數據在體量比其他序列化方法小一些吧,可以減少帶寬和流量?

說的如果json數據存儲在磁盤上,json字節數相對其他數據都偏大。

責任編輯:武曉燕 來源: JavaEdge
相關推薦

2012-02-14 10:29:02

Java

2023-06-29 08:41:02

2023-11-13 16:33:46

2011-06-01 14:50:48

2009-08-25 14:43:26

C#序列化和反序列化

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數據結構hash函數

2020-07-21 08:26:08

SpringSecurity過濾器

2021-10-20 08:05:18

Java 序列化 Java 基礎

2021-10-26 17:52:52

Android插件化技術

2021-10-20 07:18:50

Java 序列化漏洞

2025-01-13 13:00:00

Go網絡框架nbio

2011-06-01 15:05:02

序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2012-11-22 10:11:16

LispLisp教程

2019-09-09 09:05:59

圖片框架懶加載

2020-09-23 10:00:26

Redis數據庫命令

2019-06-25 10:32:19

UDP編程通信

2017-01-10 08:48:21

2024-02-21 21:14:20

編程語言開發Golang
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人福利在线观看 | 欧美 日韩 综合 | 久久国产视频播放 | 免费在线观看av的网站 | 香蕉视频91 | 国产婷婷在线视频 | 自拍亚洲 | 伊人99| 97超碰免费 | 成人免费网站 | 免费观看一级特黄欧美大片 | 涩涩视频大全 | 99久久精品国产一区二区三区 | 色综合色综合色综合 | 精品精品视频 | 欧美一级久久精品 | 国产在线一区二 | 国产成人精品综合 | 成人自拍av | 欧美性大战久久久久久久蜜臀 | 国产精品国产精品 | 在线看片国产精品 | 天天综合天天 | www.色五月.com | 欧美日韩一区二区三区不卡视频 | 国产资源网| 亚洲精品一区二区三区在线观看 | 国产精品久久久久久影院8一贰佰 | 国产一级特黄真人毛片 | 青青草原综合久久大伊人精品 | 欧美三区在线观看 | 久久精品在线播放 | 日本午夜一区 | 国产精品久久久久久久久 | 成人免费网视频 | 久久国产婷婷国产香蕉 | 久久99精品久久久久久国产越南 | 亚洲一区二区免费视频 | 国产一区二区三区久久 | 欧美在线a | 国产午夜精品久久 |