Apache Fury (incubating) ist ein rasend schnelles mehrsprachiges Serialisierungs-Framework, das auf JIT (Just-in-Time-Kompilierung) und Zero-Copy basiert und eine bis zu 170-fache Leistung und ultimative Benutzerfreundlichkeit bietet.
https://fury.apache.org
Wichtig
Apache Fury (incubating) ist ein Projekt, das derzeit bei der Apache Software Foundation (ASF) entwickelt und vom Apache Incubator PMC gesponsert wird.
Bitte lesen Sie den HAFTUNGSAUSSCHLUSS und eine vollständige Erklärung zum Thema „Inkubieren“.
Neben der sprachübergreifenden Serialisierung bietet Fury auch folgende Funktionen:
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ Externalizable
.record
werden ebenfalls unterstützt.Fury hat mehrere Binärprotokolle für verschiedene Szenarien entworfen und implementiert:
Neue Protokolle können basierend auf den vorhandenen Puffer-, Codierungs-, Meta-, Codegen- und anderen Funktionen von Fury einfach hinzugefügt werden. Alle nutzen die gleiche Codebasis und die Optimierung für ein Protokoll kann von einem anderen Protokoll wiederverwendet werden.
Verschiedene Serialisierungs-Frameworks eignen sich für unterschiedliche Szenarien, und die Benchmark-Ergebnisse dienen hier nur als Referenz.
Wenn Sie ein Benchmarking für Ihr spezifisches Szenario durchführen müssen, stellen Sie sicher, dass alle Serialisierungs-Frameworks für dieses Szenario entsprechend konfiguriert sind.
Dynamische Serialisierungs-Frameworks unterstützen Polymorphismus und Referenzen, sind jedoch im Vergleich zu statischen Serialisierungs-Frameworks oft mit höheren Kosten verbunden, es sei denn, sie nutzen JIT-Techniken wie Fury. Um genaue Benchmark-Statistiken zu gewährleisten, ist es aufgrund der Laufzeitcodegenerierung von Fury ratsam, das System vor der Datenerfassung aufzuwärmen .
In den folgenden Diagrammen stellen Titel, die „kompatibel“ enthalten, den Schemakompatibilitätsmodus dar: Typ Vorwärts-/Rückwärtskompatibilität ist aktiviert; während Titel ohne „kompatibel“ einen schemakonsistenten Modus darstellen: Das Klassenschema muss zwischen Serialisierung und Deserialisierung gleich sein.
Dabei ist Struct
eine Klasse mit 100 primitiven Feldern, MediaContent
eine Klasse von JVM-Serializern und Sample
eine Klasse von Kryo Benchmark.
Weitere Benchmarks zur Typ-Vorwärts-/Rückwärtskompatibilität, Off-Heap-Unterstützung und Zero-Copy-Serialisierung finden Sie in den Benchmarks.
Nächtlicher Schnappschuss:
< 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> -->
Release-Version:
< 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> -->
Scala2:
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
Scala3:
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
Hier geben wir einen kurzen Einstieg in die Verwendung von Fury. Weitere Informationen zu Java, Cross-Language und Zeilenformat finden Sie im Benutzerhandbuch.
Wenn Sie keine sprachübergreifenden Anforderungen haben, führt die Verwendung dieses Modus zu einer besseren Leistung.
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 ));
}
}
}
Java
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 ))
Golang
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 ])
Die Java-Objektgraph-Serialisierung von Fury unterstützt die Vorwärts-/Abwärtskompatibilität des Klassenschemas. Der Serialisierungs-Peer und der Deserialisierungs-Peer können Felder unabhängig voneinander hinzufügen/löschen.
Wir planen, die Schemakompatibilitätsunterstützung der sprachübergreifenden Serialisierung hinzuzufügen, nachdem die Metakomprimierung abgeschlossen ist.
Wir verbessern unsere Protokolle immer noch, daher ist die Binärkompatibilität zwischen den Hauptversionen von Fury derzeit nicht garantiert. Es ist jedoch zwischen Nebenversionen garantiert. Bitte versioning
Ihre Daten nach der Hauptversion von Fury, wenn Sie Fury in Zukunft aktualisieren. Weitere Informationen finden Sie unter Anleitung zum Upgrade von Fury.
Die Binärkompatibilität wird gewährleistet, wenn Fury 1.0 veröffentlicht wird.
Die statische Serialisierung ist relativ sicher. Aber dynamische Serialisierung wie die native Serialisierung von Fury Java/Python unterstützt die Deserialisierung nicht registrierter Typen, was mehr Dynamik und Flexibilität bietet, aber auch Sicherheitsrisiken mit sich bringt.
Beispielsweise kann die Deserialisierung init
-Konstruktor oder die Methode equals
/ hashCode
aufrufen. Wenn der Methodenkörper bösartigen Code enthält, ist das System gefährdet.
Fury bietet eine Klassenregistrierungsoption, die standardmäßig für solche Protokolle aktiviert ist und nur die Deserialisierung vertrauenswürdiger registrierter Typen oder integrierter Typen ermöglicht. Deaktivieren Sie die Kursregistrierung nicht, es sei denn, Sie können sicherstellen, dass Ihre Umgebung sicher ist .
Wenn diese Option deaktiviert ist, sind Sie für die Serialisierungssicherheit verantwortlich. Sie können org.apache.fury.resolver.ClassChecker
durch ClassResolver#setClassChecker
konfigurieren, um zu steuern, welche Klassen für die Serialisierung zugelassen sind.
Um in Fury gefundene Sicherheitslücken zu melden, befolgen Sie bitte den ASF-Schwachstellenmeldeprozess.
Bitte lesen Sie die BAU-Anleitung für Anweisungen zum Bauen.
Bitte lesen Sie den Leitfaden „Beitrag leisten“ für Anweisungen zum Mitwirken.
Lizenziert unter der Apache-Lizenz, Version 2.0