fury接口 (fury多功能扩展装备)

Fury 是一个极快的多语言序列化框架,由 jit(即时编译) 零拷贝 提供支持,提供高达 170 倍的性能和终极易用性。

https://furyio.org

特征

  • 多种语言 :Java/Python/C++/Golang/Javascript。
  • 零拷贝:受 pickle5和堆外读/写启发的跨语言带外序列化。
  • 高性能 :高度可扩展的 JIT 框架,可在运行时以异步多线程方式生成序列化器代码以加速序列化,通过以下方式提供 20-170 倍的加速:通过生成代码中的内联变量减少内存访问。通过生成代码中的内联调用减少虚拟方法调用。减少条件分支。减少哈希查找。
  • 多种二进制协议 :对象图、行格式等。

除了跨语言序列化之外,Fury 还具有以下功能:

  • 直接替换 JDK/Kryo/Hessian 等 Java 序列化框架,无需修改任何代码,但速度提高 100 倍。它可以极大地提高高性能RPC调用、数据传输和对象持久化的效率。
  • JDK序列化 100%兼容 ,原生支持java自定义序列化 writeObject/readObject/writeReplace/readResolve/readObjectNoData。
  • 支持 golang 的共享和循环引用对象序列化。
  • 支持 golang 自动对象序列化。

协议

不同的场景有不同的序列化要求。Fury 为这些要求设计并实现了多个二进制协议:

  • 跨语言对象图协议 :跨语言自动序列化任何对象,无需 IDL 定义、模式编译以及对象与协议之间的转换。支持共享引用和循环引用,无重复数据或递归错误。支持对象多态性。
  • 原生 java/python 对象图协议 :基于语言的类型系统进行高度优化。
  • 行格式协议 :一种缓存友好的二进制随机访问格式,支持跳过序列化和部分序列化,并且可以自动转换为列格式。

可以基于现有的缓冲区、编码、元、代码生成和其他功能轻松添加新协议。所有这些都共享相同的代码库,并且一种协议的优化可以由另一种协议重用。

基准测试

不同的序列化框架适合不同的场景,这里的基准测试结果仅供参考。

如果您需要针对特定​场景进行基准测试,请确保所有序列化框架都针对该场景进行了适当配置。

动态序列化框架支持多态性和引用,但与静态序列化框架相比,它们通常具有更高的成本,除非它们像 Fury 那样使用 JIT 技术。由于 Fury 在运行时生成代码,因此建议在收集基准统计数据之前 预热 系统。

Java序列化

标题中包含“兼容”表示架构兼容模式:支持类型向前/向后兼容。

标题中不带“兼容”的表示模式一致模式:序列化和反序列化之间的类模式必须相同。

Struct是一个具有100 个原始字段的类,是来自jvm-serializers 的MediaContent类,是来自kryo benchmark的类。Sample

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

fury最稳定的驱动,fury序列化

有关类型前向/后向兼容性、堆外支持、零拷贝序列化的更多基准测试,请参阅基准测试。

安装

爪哇

夜间快照:

<repositories>
  <repository>
    <id>sonatype</id>
    <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
    <releases>
      <enabled>false</enabled>
    </releases>
    <snapshots>
      <enabled>true</enabled>
    </snapshots>
  </repository>
</repositories>
<dependency>
  <groupId>org.furyio</groupId>
  <artifactId>fury-core</artifactId>
  <version>0.2.0-SNAPSHOT</version>
</dependency>
<!-- row/arrow format support -->
<!-- <dependency>
  <groupId>org.furyio</groupId>
  <artifactId>fury-format</artifactId>
  <version>0.2.0-SNAPSHOT</version>
</dependency> -->

发布版本:

<dependency>
  <groupId>org.furyio</groupId>
  <artifactId>fury-core</artifactId>
  <version>0.1.0</version>
</dependency>
<!-- row/arrow format support -->
<!-- <dependency>
  <groupId>org.furyio</groupId>
  <artifactId>fury-format</artifactId>
  <version>0.1.0</version>
</dependency> -->

Python

# Release version will be provided in the future.
pip install pyfury --pre

JavaScript

npm install @furyjs/fury

Golang

即将推出。

快速开始

这里我们快速入门如何使用fury,有关java、跨语言和行格式的更多详细信息请参阅用户指南。

Fury java对象图序列化

如果没有跨语言的需求,使用这种模式会有更好的性能。

import io.fury.*;
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)
        // Allow to deserialize objects unknown types, more flexible 
        // but may be insecure if the classes contains malicious code.
        // .requireClassRegistration(false)
        .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)
        // Allow to deserialize objects unknown types, more flexible 
        // but may be insecure if the classes contains malicious code.
        // .requireClassRegistration(false)
        .buildThreadSafeFury();
      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 io.fury.*;
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="io.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, "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/alipay/fury/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)
}

行格式

Java

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).getLong(5);
RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class);
// deserialize part of data.
Bar newBar = barEncoder.fromRow(barStruct);
Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20));

Python

@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 版本之间的二进制兼容性。shade如果您以后升级狂怒,请狂怒。

Fury 1.0 之前将确保二进制兼容性。

安全

静态序列化是安全的。但动态序列化如fury java/python原生序列化支持反序列化未注册类型,提供了更多的动态性和灵活性,但也引入了安全风险。

例如,反序列化时可能会调用init构造函数或equals/hashCode方法,如果方法体中包含恶意代码,系统就会面临风险。

Fury 提供了一个类注册选项,并默认为此类协议启用,该选项仅允许反序列化受信任的注册类型或内置类型。 不要禁用类注册,除非您可以确保您的环境确实安全

路线图

  • 元压缩、自动元共享和跨语言模式兼容性。
  • 用于 C++/golang/rust 的 AOT 框架静态生成代码。
  • C++/Rust 对象图序列化支持
  • Golang/Rust/NodeJS 行格式支持
  • ProtoBuffer 兼容性支持
  • 特征和知识图序列化协议
  • 不断改进我们的任何新协议的序列化基础设施

项目地址:https://github.com/alipay/fury