Apache Fury (en incubación) es un marco de serialización multilingüe increíblemente rápido impulsado por JIT (compilación justo a tiempo) y copia cero , que proporciona un rendimiento de hasta 170 veces y la máxima facilidad de uso.
https://furia.apache.org
Importante
Apache Fury (en incubación) es un esfuerzo en incubación en la Apache Software Foundation (ASF), patrocinado por Apache Incubator PMC.
Lea el DESCARGO DE RESPONSABILIDAD y una explicación completa de "incubar".
Además de la serialización en varios idiomas, Fury también aparece en:
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ API Externalizable
.record
Java 17+ también es compatible.Fury diseñó e implementó múltiples protocolos binarios para diferentes escenarios:
Se pueden agregar fácilmente nuevos protocolos basados en el búfer, la codificación, el meta, el codegen y otras capacidades existentes de Fury. Todos comparten la misma base de código y la optimización de un protocolo puede ser reutilizada por otro protocolo.
Los diferentes marcos de serialización son adecuados para diferentes escenarios y los resultados de las pruebas comparativas aquí son solo como referencia.
Si necesita realizar una evaluación comparativa para su escenario específico, asegúrese de que todos los marcos de serialización estén configurados adecuadamente para ese escenario.
Los marcos de serialización dinámica admiten polimorfismo y referencias, pero a menudo tienen un costo más alto en comparación con los marcos de serialización estáticos, a menos que utilicen técnicas JIT como lo hace Fury. Para garantizar estadísticas de referencia precisas, es recomendable calentar el sistema antes de recopilar datos debido a la generación de código de tiempo de ejecución de Fury.
En los cuadros siguientes, los títulos que contienen "compatible" representan el modo de compatibilidad con el esquema: la compatibilidad de tipos hacia adelante/hacia atrás está habilitada; mientras que los títulos sin "compatible" representan el modo consistente con el esquema: el esquema de clase debe ser el mismo entre la serialización y la deserialización.
Donde Struct
es una clase con 100 campos primitivos, MediaContent
es una clase de jvm-serializers y Sample
es una clase de kryo benchmark.
Consulte los puntos de referencia para obtener más puntos de referencia sobre compatibilidad de tipos hacia adelante/hacia atrás, compatibilidad fuera del montón y serialización sin copia.
Instantánea nocturna:
< 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> -->
Versión de lanzamiento:
< 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> -->
Escala 2:
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
Escala 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
Aquí brindamos una introducción rápida sobre cómo usar Fury; consulte la guía del usuario para obtener más detalles sobre Java, lenguajes cruzados y formato de fila.
Si no tiene requisitos de varios idiomas, usar este modo dará como resultado un mejor rendimiento.
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 ));
}
}
Pitón
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 ])
La serialización de gráficos de objetos Java de Fury admite compatibilidad hacia adelante/hacia atrás del esquema de clases. El par de serialización y el par de deserialización pueden agregar o eliminar campos de forma independiente.
Planeamos agregar el soporte de compatibilidad de esquemas de serialización entre idiomas una vez finalizada la metacompresión.
Todavía estamos mejorando nuestros protocolos, por lo que la compatibilidad binaria no está garantizada entre las versiones principales de Fury por ahora. Sin embargo, está garantizado entre versiones menores. versioning
sus datos con la versión principal de Fury si actualizará Fury en el futuro; consulte cómo actualizar Fury para obtener más detalles.
La compatibilidad binaria estará garantizada cuando se lance Fury 1.0.
La serialización estática es relativamente segura. Pero la serialización dinámica, como la serialización nativa de Fury java/python, admite la deserialización de tipos no registrados, lo que proporciona más dinámica y flexibilidad, pero también introduce riesgos de seguridad.
Por ejemplo, la deserialización puede invocar el constructor init
o el método equals
/ hashCode
; si el cuerpo del método contiene código malicioso, el sistema estará en riesgo.
Fury proporciona una opción de registro de clase que está habilitada de forma predeterminada para dichos protocolos, lo que permite solo la deserialización de tipos registrados confiables o tipos integrados. No desactive el registro de clases a menos que pueda garantizar que su entorno sea seguro .
Si esta opción está deshabilitada, usted es responsable de la seguridad de la serialización. Puede configurar org.apache.fury.resolver.ClassChecker
mediante ClassResolver#setClassChecker
para controlar qué clases están permitidas para la serialización.
Para informar vulnerabilidades de seguridad encontradas en Fury, siga el proceso de informe de vulnerabilidades de ASF.
Lea la guía BUILD para obtener instrucciones sobre cómo construir.
Lea la guía CONTRIBUCIÓN para obtener instrucciones sobre cómo contribuir.
Licenciado bajo la Licencia Apache, Versión 2.0