Apache Fury(正在孵化)是一种速度极快的多语言序列化框架,由JIT (即时编译)和零复制提供支持,可提供高达 170 倍的性能和终极易用性。
https://fury.apache.org
重要的
Apache Fury(孵化中)是在 Apache 软件基金会 (ASF) 中进行孵化的一项成果,由 Apache 孵化器 PMC 赞助。
请阅读免责声明和“孵化”的完整解释。
除了跨语言序列化之外,Fury 还具有以下功能:
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ Externalizable
API。record
。Fury针对不同场景设计并实现了多种二进制协议:
基于Fury现有的缓冲区、编码、元、代码生成等功能,可以轻松添加新协议。所有这些都共享相同的代码库,并且一种协议的优化可以由另一种协议重用。
不同的序列化框架适合不同的场景,这里的基准测试结果仅供参考。
如果您需要针对特定场景进行基准测试,请确保所有序列化框架都针对该场景进行了适当配置。
动态序列化框架支持多态性和引用,但与静态序列化框架相比,它们通常具有更高的成本,除非它们像 Fury 那样使用 JIT 技术。为了确保准确的基准统计数据,建议在由于 Fury 的运行时代码生成而收集数据之前预热系统。
在下面的这些图表中,包含“兼容”的标题代表模式兼容模式:类型向前/向后兼容性已启用;而没有“兼容”的标题表示模式一致模式:序列化和反序列化之间的类模式必须相同。
其中Struct
是一个具有 100 个原始字段的类, MediaContent
是来自 jvm-serializers 的类,而Sample
是来自 kryo benchmark 的类。
有关类型前向/后向兼容性、堆外支持、零拷贝序列化的更多基准测试,请参阅基准测试。
夜间快照:
< repositories >
< repository >
< id >apache</ id >
< url >https://repository.apache.org/snapshots/</ url >
< releases >
< enabled >false</ enabled >
</ releases >
< snapshots >
< enabled >true</ enabled >
</ snapshots >
</ repository >
</ repositories >
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-core</ artifactId >
< version >0.10.0-SNAPSHOT</ version >
</ dependency >
<!-- row/arrow format support -->
<!-- <dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-format</artifactId>
<version>0.10.0-SNAPSHOT</version>
</dependency> -->
发布版本:
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-core</ artifactId >
< version >0.9.0</ version >
</ dependency >
<!-- row/arrow format support -->
<!-- <dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-format</artifactId>
<version>0.9.0</version>
</dependency> -->
斯卡拉2:
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
斯卡拉3:
libraryDependencies += " org.apache.fury " % " fury-scala_3 " % " 0.9.0 "
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-kotlin</ artifactId >
< version >0.9.0</ version >
</ dependency >
pip install pyfury
npm install @furyjs/fury
go get github.com/apache/fury/go/fury
这里我们快速入门如何使用 Fury,有关 java、跨语言和行格式的更多详细信息请参阅用户指南。
如果你没有跨语言的需求,使用这种模式会带来更好的性能。
import org . apache . fury .*;
import org . apache . fury . config .*;
import java . util .*;
public class Example {
public static void main ( String [] args ) {
SomeClass object = new SomeClass ();
// Note that Fury instances should be reused between
// multiple serializations of different objects.
{
Fury fury = Fury . builder (). withLanguage ( Language . JAVA )
. requireClassRegistration ( true )
. build ();
// Registering types can reduce class name serialization overhead, but not mandatory.
// If class registration enabled, all custom types must be registered.
fury . register ( SomeClass . class );
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
{
ThreadSafeFury fury = Fury . builder (). withLanguage ( Language . JAVA )
. requireClassRegistration ( true )
. buildThreadSafeFury ();
// Registering types can reduce class name serialization overhead, but not mandatory.
// If class registration enabled, all custom types must be registered.
fury . register ( SomeClass . class );
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
{
ThreadSafeFury fury = new ThreadLocalFury ( classLoader -> {
Fury f = Fury . builder (). withLanguage ( Language . JAVA )
. withClassLoader ( classLoader ). build ();
f . register ( SomeClass . class );
return f ;
});
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
}
}
爪哇
import org . apache . fury .*;
import org . apache . fury . config .*;
import java . util .*;
public class ReferenceExample {
public static class SomeClass {
SomeClass f1 ;
Map < String , String > f2 ;
Map < String , String > f3 ;
}
public static Object createObject () {
SomeClass obj = new SomeClass ();
obj . f1 = obj ;
obj . f2 = ofHashMap ( "k1" , "v1" , "k2" , "v2" );
obj . f3 = obj . f2 ;
return obj ;
}
// mvn exec:java -Dexec.mainClass="org.apache.fury.examples.ReferenceExample"
public static void main ( String [] args ) {
Fury fury = Fury . builder (). withLanguage ( Language . XLANG )
. withRefTracking ( 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 ]
fury = pyfury . Fury ( ref_tracking = True )
fury . register_class ( SomeClass , type_tag = "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 ))
戈兰
package main
import furygo "github.com/apache/fury/go/fury"
import "fmt"
func main () {
type SomeClass struct {
F1 * SomeClass
F2 map [ string ] string
F3 map [ string ] string
}
fury := furygo . 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 )
}
public class Bar {
String f1 ;
List < Long > f2 ;
}
public class Foo {
int f1 ;
List < Integer > f2 ;
Map < String , Integer > f3 ;
List < Bar > f4 ;
}
RowEncoder < Foo > encoder = Encoders . bean ( Foo . class );
Foo foo = new Foo ();
foo . f1 = 10 ;
foo . f2 = IntStream . range ( 0 , 1000000 ). boxed (). collect ( Collectors . toList ());
foo . f3 = IntStream . range ( 0 , 1000000 ). boxed (). collect ( Collectors . toMap ( i -> "k" + i , i -> i ));
List < Bar > bars = new ArrayList <>( 1000000 );
for ( int i = 0 ; i < 1000000 ; i ++) {
Bar bar = new Bar ();
bar . f1 = "s" + i ;
bar . f2 = LongStream . range ( 0 , 10 ). boxed (). collect ( Collectors . toList ());
bars . add ( bar );
}
foo . f4 = bars ;
// Can be zero-copy read by python
BinaryRow binaryRow = encoder . toRow ( foo );
// can be data from python
Foo newFoo = encoder . fromRow ( binaryRow );
// zero-copy read List<Integer> f2
BinaryArray binaryArray2 = binaryRow . getArray ( 1 );
// zero-copy read List<Bar> f4
BinaryArray binaryArray4 = binaryRow . getArray ( 3 );
// zero-copy read 11th element of `readList<Bar> f4`
BinaryRow barStruct = binaryArray4 . getStruct ( 10 );
// zero-copy read 6th of f2 of 11th element of `readList<Bar> f4`
barStruct . getArray ( 1 ). getInt64 ( 5 );
RowEncoder < Bar > barEncoder = Encoders . bean ( Bar . class );
// deserialize part of data.
Bar newBar = barEncoder . fromRow ( barStruct );
Bar newBar2 = barEncoder . fromRow ( binaryArray4 . getStruct ( 20 ));
@ dataclass
class Bar :
f1 : str
f2 : List [ pa . int64 ]
@ dataclass
class Foo :
f1 : pa . int32
f2 : List [ pa . int32 ]
f3 : Dict [ str , pa . int32 ]
f4 : List [ Bar ]
encoder = pyfury . encoder ( Foo )
foo = Foo ( f1 = 10 , f2 = list ( range ( 1000_000 )),
f3 = { f"k { i } " : i for i in range ( 1000_000 )},
f4 = [ Bar ( f1 = f"s { i } " , f2 = list ( range ( 10 ))) for i in range ( 1000_000 )])
binary : bytes = encoder . to_row ( foo ). to_bytes ()
foo_row = pyfury . RowData ( encoder . schema , binary )
print ( foo_row . f2 [ 100000 ], foo_row . f4 [ 100000 ]. f1 , foo_row . f4 [ 200000 ]. f2 [ 5 ])
Fury java对象图序列化支持类模式向前/向后兼容性。序列化对等体和反序列化对等体可以独立添加/删除字段。
我们计划在元压缩完成后添加跨语言序列化的模式兼容性支持。
我们仍在改进我们的协议,因此目前无法保证 Fury 主要版本之间的二进制兼容性。但是,小版本之间是有保证的。如果您将来要升级 Fury,请按 Fury 主要版本对您的数据versioning
,请参阅如何升级 Fury 了解更多详细信息。
Fury 1.0 发布时将保证二进制兼容性。
静态序列化相对安全。但动态序列化如Fury java/python原生序列化支持反序列化未注册类型,提供了更多的动态性和灵活性,但也引入了安全风险。
例如,反序列化可能会调用init
构造函数或equals
/ hashCode
方法,如果方法体中包含恶意代码,系统就会面临风险。
Fury 提供了一个类注册选项,默认情况下为此类协议启用,仅允许反序列化受信任的注册类型或内置类型。不要禁用类注册,除非您可以确保您的环境是安全的。
如果禁用此选项,您将负责序列化安全。您可以通过ClassResolver#setClassChecker
配置org.apache.fury.resolver.ClassChecker
来控制哪些类允许序列化。
要报告 Fury 中发现的安全漏洞,请遵循 ASF 漏洞报告流程。
请阅读构建指南以获取有关如何构建的说明。
请阅读贡献指南以获取有关如何贡献的说明。
根据 Apache 许可证 2.0 版获得许可