Apache Fury(인큐베이팅) 는 JIT (Just-In-Time 컴파일) 및 제로 복사를 기반으로 하는 매우 빠른 다중 언어 직렬화 프레임워크로, 최대 170배의 성능과 최고의 사용 편의성을 제공합니다.
https://fury.apache.org
중요한
Apache Fury(인큐베이팅)는 Apache Incubator PMC의 후원을 받아 ASF(Apache Software Foundation)에서 인큐베이션을 진행 중인 노력입니다.
면책조항과 "인큐베이팅"에 대한 자세한 설명을 읽어보세요.
언어 간 직렬화 외에도 Fury는 다음 기능도 제공합니다.
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ Externalizable
API를 지원합니다.record
도 지원됩니다.Fury는 다양한 시나리오를 위해 여러 바이너리 프로토콜을 설계하고 구현했습니다.
Fury의 기존 버퍼, 인코딩, 메타, 코드 생성 및 기타 기능을 기반으로 새로운 프로토콜을 쉽게 추가할 수 있습니다. 이들 모두는 동일한 코드베이스를 공유하며 한 프로토콜의 최적화는 다른 프로토콜에서 재사용될 수 있습니다.
다양한 직렬화 프레임워크는 다양한 시나리오에 적합하며 여기의 벤치마크 결과는 참조용일 뿐입니다.
특정 시나리오를 벤치마킹해야 하는 경우 모든 직렬화 프레임워크가 해당 시나리오에 맞게 구성되었는지 확인하세요.
동적 직렬화 프레임워크는 다형성 및 참조를 지원하지만 Fury와 같은 JIT 기술을 활용하지 않는 한 정적 직렬화 프레임워크에 비해 비용이 더 높은 경우가 많습니다. 정확한 벤치마크 통계를 보장하려면 Fury의 런타임 코드 생성으로 인해 데이터를 수집하기 전에 시스템을 워밍업하는 것이 좋습니다.
아래 차트에서 "호환"이 포함된 제목은 스키마 호환 모드를 나타냅니다. 유형의 정방향/역방향 호환성이 활성화됩니다. "호환"이 없는 제목은 스키마 일관성 모드를 나타냅니다. 클래스 스키마는 직렬화와 역직렬화 간에 동일해야 합니다.
Struct
100개의 기본 필드가 있는 클래스이고 MediaContent
jvm-serializers의 클래스이며 Sample
kryo 벤치마크의 클래스입니다.
형식 순방향/역방향 호환성, 오프힙 지원, 제로 복사 직렬화에 대한 더 많은 벤치마크는 벤치마크를 참조하세요.
야간 스냅샷:
< 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 ));
}
}
파이썬
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 취약점 보고 프로세스를 따르십시오.
빌드 방법에 대한 지침은 BUILD 가이드를 읽어보세요.
기여 방법에 대한 지침은 CONTRIBUTING 가이드를 읽어보세요.
Apache 라이센스 버전 2.0에 따라 라이센스가 부여되었습니다.