新聞中心
引言
過(guò)去十多年大數(shù)據(jù)和分布式系統(tǒng)蓬勃發(fā)展,序列化是其頻繁使用的技術(shù)。當(dāng)對(duì)象需要跨進(jìn)程、跨語(yǔ)言、跨節(jié)點(diǎn)傳輸、持久化、狀態(tài)讀寫(xiě)時(shí),都需要進(jìn)行序列化,其性能和易用性影響著系統(tǒng)的運(yùn)行效率和開(kāi)發(fā)效率。

成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)公司,提供成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);可快速的進(jìn)行網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,是專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
對(duì)于Java序列化,盡管Kryo[1]等框架提供了相比JDK序列化數(shù)倍的性能,對(duì)于高吞吐、低延遲、大規(guī)模數(shù)據(jù)傳輸場(chǎng)景,序列化仍然是整個(gè)系統(tǒng)的性能瓶頸。為了優(yōu)化序列化的性能,分布式系統(tǒng)如Spark[2]、Flink[3]使用了專有行列存二進(jìn)制格式如tungsten[4]和arrow[5]。這些格式減少了序列化開(kāi)銷,但增加了系統(tǒng)的復(fù)雜性,犧牲了編程的靈活性,同時(shí)也只覆蓋了SQL等關(guān)系代數(shù)計(jì)算專有場(chǎng)景。對(duì)于通用分布式編程和跨進(jìn)程通信,序列化性能始終是一個(gè)繞不過(guò)去的關(guān)鍵問(wèn)題。
同時(shí)隨著計(jì)算和應(yīng)用場(chǎng)景的日益復(fù)雜化,系統(tǒng)已經(jīng)從單一語(yǔ)言的編程范式發(fā)展到多語(yǔ)言融合編程,對(duì)象在語(yǔ)言之間傳輸?shù)?strong>易用性影響著系統(tǒng)開(kāi)發(fā)效率,進(jìn)而影響業(yè)務(wù)的迭代效率。而已有的跨語(yǔ)言序列化框架protobuf/flatbuffer/msgpack等由于無(wú)法支持引用、不支持Zero-Copy、大量手寫(xiě)代碼以及生成的類不符合面向?qū)ο笤O(shè)計(jì)[6]無(wú)法給類添加行為,導(dǎo)致在易用性、靈活性、動(dòng)態(tài)性和性能上的不足,并不能滿足通用跨語(yǔ)言編程需求。
基于此,我們開(kāi)發(fā)了Fury,通過(guò)一套支持引用、類型嵌入的語(yǔ)言無(wú)關(guān)協(xié)議,以及JIT動(dòng)態(tài)編譯加速、緩存優(yōu)化和Zero-Copy等技術(shù),實(shí)現(xiàn)了任意對(duì)象像動(dòng)態(tài)語(yǔ)言自動(dòng)序列化一樣跨語(yǔ)言自動(dòng)序列化,消除了語(yǔ)言之間的編程邊界,并提供相比于業(yè)界別的框架最高20~200倍的性能。
Fury是什么
Fury是一個(gè)基于JIT的高性能多語(yǔ)言原生序列化框架,專注于提供極致的序列化性能和易用性:
- 支持主流編程語(yǔ)言如Java/Python/C++/Golang,其它語(yǔ)言可輕易擴(kuò)展;
- 多語(yǔ)言/跨語(yǔ)言自動(dòng)序列化任意對(duì)象,無(wú)需創(chuàng)建IDL文件、手動(dòng)編譯schema生成代碼以及將對(duì)象轉(zhuǎn)換為中間格式;
- 多語(yǔ)言/跨語(yǔ)言自動(dòng)序列化共享引用和循環(huán)引用,用戶只需要關(guān)心對(duì)象,不需要關(guān)心數(shù)據(jù)重復(fù)或者遞歸錯(cuò)誤;
- 基于JIT動(dòng)態(tài)編譯技術(shù)在運(yùn)行時(shí)自動(dòng)生成序列化代碼優(yōu)化性能,增加方法內(nèi)聯(lián)、代碼緩存和死代碼消除,減少虛方法調(diào)用/條件分支/Hash查找/元數(shù)據(jù)寫(xiě)入等,提供相比其它序列化框架20~200倍以上的性能;
- Zero-Copy序列化支持,支持Out of band序列化協(xié)議,支持堆外內(nèi)存讀寫(xiě);
- 提供緩存友好的二進(jìn)制隨機(jī)訪問(wèn)行存格式,支持跳過(guò)序列化和部分序列化,并能和列存自動(dòng)互轉(zhuǎn);
除了跨語(yǔ)言能力,F(xiàn)ury還具備以下能力:
- 無(wú)縫替代JDK/Kryo/Hessian等Java序列化框架,無(wú)需修改任何代碼,同時(shí)提供相比Kryo 20倍以上的性能,相比Hessian100倍以上的性能,相比JDK自帶序列化200倍以上的性能,可以大幅提升高性能場(chǎng)景RPC調(diào)用和對(duì)象持久化效率;
- 支持共享引用和循環(huán)引用的Golang序列化框架;
- 支持對(duì)象自動(dòng)序列化的Golang序列化框架;
目前Fury已經(jīng)支持Java、Python、Golang以及C++。本文將首先簡(jiǎn)單介紹如何使用Fury,然后將Fury跟別的序列化框架進(jìn)行功能、性能和易用性比較,F(xiàn)ury的實(shí)現(xiàn)原理將在后續(xù)文章里面詳細(xì)介紹。
如何使用Fury
這里給出跨語(yǔ)言序列化、純Java序列化以及避免序列化的示例:
- 跨語(yǔ)言序列化自定義類型
- 跨語(yǔ)言序列化包含循環(huán)引用的自定義類型
- 跨語(yǔ)言零拷貝序列化
- Drop-in替代Kryo/Hession/JDK序列化
通過(guò)Fury Format避免序列化
序列化自定義類型
下面是序列化用戶自定義類型的一個(gè)示例,該類型里面包含多個(gè)基本類型以及嵌套類型的字段,在業(yè)務(wù)應(yīng)用里面相當(dāng)常見(jiàn)。需要注意自定義類型跨語(yǔ)言序列化之前需要調(diào)用`register`API注冊(cè)自定義類型,建立類型在不同語(yǔ)言之間的映射關(guān)系,同時(shí)保證GoLang等靜態(tài)語(yǔ)言編譯器編譯代碼時(shí)不裁剪掉這部分類型的符號(hào)。
Java序列化示例
apf2;
}
public static class SomeClass2 {
Object f1;
String f2;
List
純Java序列化:
public class CustomObjectExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.CustomObjectExample"
public static void main(String[] args) {
// Fury應(yīng)該在多個(gè)對(duì)象序列化之間復(fù)用,不要每次創(chuàng)建新的Fury實(shí)例
Fury fury = Fury.builder().withLanguage(Language.JAVA)
.withReferenceTracking(false)
.withClassRegistrationRequired(false)
.build();
byte[] bytes = fury.serialize(createObject());
System.out.println(fury.deserialize(bytes));;
}
}跨語(yǔ)言序列化:
public class CustomObjectExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.CustomObjectExample"
public static void main(String[] args) {
// Fury應(yīng)該在多個(gè)對(duì)象序列化之間復(fù)用,不要每次創(chuàng)建新的Fury實(shí)例
Fury fury = Fury.builder().withLanguage(Language.XLANG)
.withReferenceTracking(false).build();
fury.register(SomeClass1.class, "example.SomeClass1");
fury.register(SomeClass2.class, "example.SomeClass2");
byte[] bytes = fury.serialize(createObject());
// bytes can be data serialized by other languages.
System.out.println(fury.deserialize(bytes));;
}
}Python序列化示例
from dataclasses import dataclass
from typing import List, Dict
import pyfury
@dataclass
class SomeClass2:
f1: Any = None
f2: str = None
f3: List[str] = None
f4: Dict[pyfury.Int8Type, pyfury.Int32Type] = None
f5: pyfury.Int8Type = None
f6: pyfury.Int16Type = None
f7: pyfury.Int32Type = None
# int類型默認(rèn)會(huì)按照l(shuí)ong類型進(jìn)行序列化,如果對(duì)端是更加narrow的類型,
# 需要使用pyfury.Int32Type等進(jìn)行標(biāo)注
f8: int = None # 也可以使用pyfury.Int64Type進(jìn)行標(biāo)注
f9: pyfury.Float32Type = None
f10: float = None # 也可以使用pyfury.Float64Type進(jìn)行標(biāo)注
f11: pyfury.Int16ArrayType = None
f12: List[pyfury.Int16Type] = None
@dataclass
class SomeClass1:
f1: Any
f2: Dict[pyfury.Int8Type, pyfury.Int32Type]
if __name__ == "__main__":
fury_ = pyfury.Fury(reference_tracking=False)
fury_.register_class(SomeClass1, "example.SomeClass1")
fury_.register_class(SomeClass2, "example.SomeClass2")
obj2 = SomeClass2(f1=True, f2={-1: 2})
obj1 = SomeClass1(
f1=obj2,
f2="abc",
f3=["abc", "abc"],
f4={1: 2},
f5=2 ** 7 - 1,
f6=2 ** 15 - 1,
f7=2 ** 31 - 1,
f8=2 ** 63 - 1,
f9=1.0 / 2,
f10=1 / 3.0,
f11=array.array("h", [1, 2]),
f12=[-1, 4],
)
data = fury_.serialize(obj)
# bytes can be data serialized by other languages.
print(fury_.deserialize(data))
GoLang序列化示例
package main
import "code.alipay.com/ray-project/fury/go/fury"
import "fmt"
func main() {
type SomeClass1 struct {
F1 interface{}
F2 string
F3 []interface{}
F4 map[int8]int32
F5 int8
F6 int16
F7 int32
F8 int64
F9 float32
F10 float64
F11 []int16
F12 fury.Int16Slice
}
type SomeClas2 struct {
F1 interface{}
F2 map[int8]int32
}
fury_ := fury.NewFury(false)
if err := fury_.RegisterTagType("example.SomeClass1", SomeClass1{}); err != nil {
panic(err)
}
if err := fury_.RegisterTagType("example.SomeClass2", SomeClass2{}); err != nil {
panic(err)
}
obj2 := &SomeClass2{}
obj2.F1 = true
obj2.F2 = map[int8]int32{-1: 2}
obj := &SomeClass1{}
obj.F1 = obj2
obj.F2 = "abc"
obj.F3 = []interface{}{"abc", "abc"}
f4 := map[int8]int32{1: 2}
obj.F4 = f4
obj.F5 = fury.MaxInt8
obj.F6 = fury.MaxInt16
obj.F7 = fury.MaxInt32
obj.F8 = fury.MaxInt64
obj.F9 = 1.0 / 2
obj.F10 = 1 / 3.0
obj.F11 = []int16{1, 2}
obj.F12 = []int16{-1, 4}
bytes, err := fury_.Marshal(value)
if err != nil {
}
var newValue interface{}
// bytes can be data serialized by other languages.
if err := fury_.Unmarshal(bytes, &newValue); err != nil {
panic(err)
}
fmt.Println(newValue)
}
序列化共享&循環(huán)引用
共享引用和循環(huán)引用是程序里面常見(jiàn)的構(gòu)造,很多數(shù)據(jù)結(jié)構(gòu)如圖都包含大量的循環(huán)引用,而手動(dòng)實(shí)現(xiàn)這些包含共享引用和循環(huán)引用的對(duì)象,需要大量冗長(zhǎng)復(fù)雜易出錯(cuò)的代碼。跨語(yǔ)言序列化框架支持循環(huán)引用可以極大簡(jiǎn)化這些復(fù)雜場(chǎng)景的序列化,加速業(yè)務(wù)迭代效率。下面是一個(gè)包含循環(huán)引用的自定義類型跨語(yǔ)言序列化示例。
Java序列化示例
import com.google.common.collect.ImmutableMap;
import io.fury.*;
import java.util.Map;
public class ReferenceExample {
public static class SomeClass {
SomeClass f1;
Mapf2;
Mapf3;
}
public static Object createObject() {
SomeClass obj = new SomeClass();
obj.f1 = obj;
obj.f2 = ImmutableMap.of("k1", "v1", "k2", "v2");
obj.f3 = obj.f2;
return obj;
}
}
Java序列化:
public class ReferenceExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.ReferenceExample"
public static void main(String[] args) {
// Fury應(yīng)該在多個(gè)對(duì)象序列化之間復(fù)用,不要每次創(chuàng)建新的Fury實(shí)例
Fury fury = Fury.builder().withLanguage(Language.JAVA)
.withReferenceTracking(true)
.withClassRegistrationRequired(false)
.build();
byte[] bytes = fury.serialize(createObject());
System.out.println(fury.deserialize(bytes));;
}
}跨語(yǔ)言序列化:
public class ReferenceExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.ReferenceExample"
public static void main(String[] args) {
// Fury應(yīng)該在多個(gè)對(duì)象序列化之間復(fù)用,不要每次創(chuàng)建新的Fury實(shí)例
Fury fury = Fury.builder().withLanguage(Language.XLANG)
.withReferenceTracking(true).build();
fury.register(SomeClass.class, "example.SomeClass");
byte[] bytes = fury.serialize(createObject());
// bytes can be data serialized by other languages.
System.out.println(fury.deserialize(bytes));;
}
}Python序列化示例
from typing import Dict
import pyfury
class SomeClass:
f1: "SomeClass"
f2: Dict[str, str]
f3: Dict[str, str]
if __name__ == "__main__":
fury_ = pyfury.Fury(reference_tracking=True)
fury_.register_class(SomeClass, "example.SomeClass")
obj = SomeClass()
obj.f2 = {"k1": "v1", "k2": "v2"}
obj.f1, obj.f3 = obj, obj.f2
data = fury_.serialize(obj)
# bytes can be data serialized by other languages.
print(fury_.deserialize(data))
Golang序列化示例
package main
import "code.alipay.com/ray-project/fury/go/fury"
import "fmt"
func main() {
type SomeClass struct {
F1 *SomeClass
F2 map[string]string
F3 map[string]string
}
fury_ := fury.NewFury(true)
if err := fury_.RegisterTagType("example.SomeClass", SomeClass{}); err != nil {
panic(err)
}
value := &SomeClass{F2: map[string]string{"k1": "v1", "k2": "v2"}}
value.F3 = value.F2
value.F1 = value
bytes, err := fury_.Marshal(value)
if err != nil {
}
var newValue interface{}
// bytes can be data serialized by other languages.
if err := fury_.Unmarshal(bytes, &newValue); err != nil {
panic(err)
}
fmt.Println(newValue)
}
Zero-Copy序列化
對(duì)于大規(guī)模數(shù)據(jù)傳輸場(chǎng)景,內(nèi)存拷貝有時(shí)會(huì)成為整個(gè)系統(tǒng)的瓶頸。為此各種語(yǔ)言和框架做了大量?jī)?yōu)化,比如Java提供了NIO能力,避免了內(nèi)存在用戶態(tài)和內(nèi)核態(tài)之間的來(lái)回拷貝;Kafka使用Java的NIO來(lái)實(shí)現(xiàn)零拷貝;Python Pickle5提供了Out-Of-Band Buffer[7]序列化能力來(lái)避免額外拷貝。對(duì)于高性能跨語(yǔ)言數(shù)據(jù)傳輸,序列化框架也需要能夠支持Zero-Copy,避免數(shù)據(jù)Buffer的額外拷貝。下面是一個(gè)Fury序列化多個(gè)基本類型數(shù)組組成的對(duì)象樹(shù)的示例,分別對(duì)應(yīng)到Java基本類型數(shù)組、Python Numpy數(shù)組、Golang 基本類型slice。對(duì)于ByteBuffer零拷貝,在本文的性能測(cè)試部分也給出了部分介紹。
Java序列化示例
Java序列化
import io.fury.*;
import io.fury.serializers.BufferObject;
import io.fury.memory.MemoryBuffer;
import java.util.*;
import java.util.stream.Collectors;
public class ZeroCopyExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.ZeroCopyExample"
public static void main(String[] args) {
// Fury應(yīng)該在多個(gè)對(duì)象序列化之間復(fù)用,不要每次創(chuàng)建新的Fury實(shí)例
Fury fury = Fury.builder()
.withLanguage(Language.JAVA)
.withClassRegistrationRequired(false)
.build();
List


咨詢
建站咨詢
