Apache Fury (incubação) é uma estrutura de serialização multilíngue extremamente rápida alimentada por JIT (compilação just-in-time) e cópia zero , fornecendo desempenho de até 170x e facilidade de uso final.
https://fury.apache.org
Importante
Apache Fury (incubação) é um esforço em incubação na Apache Software Foundation (ASF), patrocinado pela Apache Incubator PMC.
Por favor, leia o AVISO LEGAL e uma explicação completa sobre "incubação".
Além da serialização entre idiomas, Fury também apresenta:
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ API Externalizable
.record
Java 17+ também é compatível.Fury projetou e implementou vários protocolos binários para diferentes cenários:
Novos protocolos podem ser facilmente adicionados com base no buffer, codificação, meta, codegen e outros recursos existentes do Fury. Todos compartilham a mesma base de código e a otimização de um protocolo pode ser reutilizada por outro protocolo.
Diferentes estruturas de serialização são adequadas para diferentes cenários, e os resultados de benchmark aqui são apenas para referência.
Se você precisar fazer uma avaliação comparativa para seu cenário específico, certifique-se de que todas as estruturas de serialização estejam configuradas adequadamente para esse cenário.
Estruturas de serialização dinâmica suportam polimorfismo e referências, mas geralmente têm um custo mais alto em comparação com estruturas de serialização estática, a menos que utilizem técnicas JIT como o Fury. Para garantir estatísticas de benchmark precisas, é aconselhável aquecer o sistema antes de coletar dados devido à geração de código de tempo de execução do Fury.
Nos gráficos abaixo, os títulos que contêm "compatível" representam o modo compatível com o esquema: o tipo de compatibilidade direta/regressiva está ativado; enquanto os títulos sem "compatível" representam o modo consistente do esquema: o esquema da classe deve ser o mesmo entre serialização e desserialização.
Onde Struct
é uma classe com 100 campos primitivos, MediaContent
é uma classe de serializadores jvm e Sample
é uma classe de benchmark kryo.
Consulte benchmarks para obter mais benchmarks sobre compatibilidade direta/regressiva de tipo, suporte off-heap e serialização de cópia zero.
Instantâneo noturno:
< 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> -->
Versão de lançamento:
< 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> -->
Escala2:
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
Escala3:
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
Aqui damos um início rápido sobre como usar o Fury, consulte o guia do usuário para obter mais detalhes sobre java, linguagem cruzada e formato de linha.
Se você não tiver requisitos de vários idiomas, usar esse modo resultará em melhor desempenho.
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ão
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 ])
A serialização do gráfico de objeto Fury Java oferece suporte à compatibilidade direta / retroativa do esquema de classe. O par de serialização e o par de desserialização podem adicionar/excluir campos de forma independente.
Planejamos adicionar o suporte de compatibilidade de esquema de serialização entre idiomas após a conclusão da metacompactação.
Ainda estamos melhorando nossos protocolos, portanto a compatibilidade binária não é garantida entre os principais lançamentos do Fury por enquanto. No entanto, é garantido entre versões secundárias. Por favor, versioning
seus dados pela versão principal do Fury se você atualizar o Fury no futuro, veja como atualizar o Fury para obter mais detalhes.
A compatibilidade binária será garantida quando o Fury 1.0 for lançado.
A serialização estática é relativamente segura. Mas a serialização dinâmica, como a serialização nativa Fury java/python, suporta a desserialização de tipos não registrados, o que fornece mais dinâmica e flexibilidade, mas também apresenta riscos de segurança.
Por exemplo, a desserialização pode invocar o construtor init
ou o método equals
/ hashCode
, se o corpo do método contiver código malicioso, o sistema estará em risco.
Fury fornece uma opção de registro de classe habilitada por padrão para tais protocolos, permitindo apenas a desserialização de tipos registrados confiáveis ou tipos integrados. Não desative o registro de turma, a menos que você possa garantir que seu ambiente seja seguro .
Se esta opção estiver desabilitada, você será responsável pela segurança da serialização. Você pode configurar org.apache.fury.resolver.ClassChecker
por ClassResolver#setClassChecker
para controlar quais classes são permitidas para serialização.
Para relatar vulnerabilidades de segurança encontradas no Fury, siga o processo de relatório de vulnerabilidades do ASF.
Por favor, leia o guia BUILD para obter instruções sobre como construir.
Por favor, leia o guia CONTRIBUINDO para obter instruções sobre como contribuir.
Licenciado sob a Licença Apache, Versão 2.0