跳到主要内容
版本:0.14

跨语言序列化

Apache Fory™ 通过 xlang 序列化格式支持 Java 和其他语言(Python、Rust、Go、JavaScript 等)之间的无缝数据交换。这使得多语言微服务、多语言数据管道和跨平台数据共享成为可能。

启用跨语言模式

要序列化供其他语言使用的数据,使用 Language.XLANG 模式:

import org.apache.fory.*;
import org.apache.fory.config.*;

// 使用 XLANG 模式创建 Fory 实例
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true) // 为复杂图启用引用跟踪
.build();

为跨语言兼容性注册类型

类型必须使用一致的 ID 或名称在所有语言中注册。Fory 支持两种注册方法:

按 ID 注册(推荐用于性能)

public record Person(String name, int age) {}

// 使用数字 ID 注册 - 更快更紧凑
fory.register(Person.class, 1);

Person person = new Person("Alice", 30);
byte[] bytes = fory.serialize(person);
// bytes 可以被 Python、Rust、Go 等反序列化。

优点:更快的序列化,更小的二进制大小 权衡:需要协调以避免跨团队/服务的 ID 冲突

按名称注册(推荐用于灵活性)

public record Person(String name, int age) {}

// 使用字符串名称注册 - 更灵活
fory.register(Person.class, "example.Person");

Person person = new Person("Alice", 30);
byte[] bytes = fory.serialize(person);
// bytes 可以被 Python、Rust、Go 等反序列化。

优点:不容易冲突,跨团队管理更容易,无需协调 权衡:由于字符串编码,二进制大小稍大

跨语言示例:Java ↔ Python

Java(序列化器)

import org.apache.fory.*;
import org.apache.fory.config.*;

public record Person(String name, int age) {}

public class Example {
public static void main(String[] args) {
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
.build();

// 使用一致的名称注册
fory.register(Person.class, "example.Person");

Person person = new Person("Bob", 25);
byte[] bytes = fory.serialize(person);

// 通过网络/文件/队列将 bytes 发送到 Python 服务
}
}

Python(反序列化器)

import pyfory
from dataclasses import dataclass

@dataclass
class Person:
name: str
age: pyfory.int32

# 在 xlang 模式下创建 Fory
fory = pyfory.Fory(ref_tracking=True)

# 使用与 Java 相同的名称注册
fory.register_type(Person, typename="example.Person")

# 从 Java 反序列化 bytes
person = fory.deserialize(bytes_from_java)
print(f"{person.name}, {person.age}") # 输出:Bob, 25

处理循环引用和共享引用

启用引用跟踪时,跨语言模式支持循环引用和共享引用:

public class Node {
public String value;
public Node next;
public Node parent;
}

Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true) // 循环引用需要
.build();

fory.register(Node.class, "example.Node");

// 创建循环引用
Node node1 = new Node();
node1.value = "A";
Node node2 = new Node();
node2.value = "B";
node1.next = node2;
node2.parent = node1; // 循环引用

byte[] bytes = fory.serialize(node1);
// Python/Rust/Go 可以正确反序列化这个,并保留循环引用

类型映射考虑

并非所有 Java 类型在其他语言中都有等价物。使用 xlang 模式时:

  • 使用原始类型intlongdoubleString)以获得最大兼容性
  • 使用标准集合ListMapSet)而不是特定于语言的集合
  • 避免 Java 特定类型,如 OptionalBigDecimal(除非目标语言支持它们)
  • 查看类型映射指南获取完整的兼容性矩阵

兼容类型

public record UserData(
String name, // ✅ 兼容
int age, // ✅ 兼容
List<String> tags, // ✅ 兼容
Map<String, Integer> scores // ✅ 兼容
) {}

有问题的类型

public record UserData(
Optional<String> name, // ❌ 不跨语言兼容
BigDecimal balance, // ❌ 支持有限
EnumSet<Status> statuses // ❌ Java 特定集合
) {}

性能考虑

跨语言模式相比仅 Java 模式有额外的开销:

  • 类型元数据编码:每个类型添加额外字节
  • 类型解析:在反序列化期间需要名称/ID 查找

为了最佳性能

  • 尽可能使用基于 ID 的注册(更小的编码)
  • 如果不需要循环引用,禁用引用跟踪withRefTracking(false)
  • 当只需要 Java 序列化时使用 Java 模式Language.JAVA

跨语言最佳实践

  1. 一致的注册:确保所有服务使用相同的 ID/名称注册类型
  2. 版本兼容性:使用兼容模式跨服务进行 schema 演化

跨语言序列化故障排除

"Type not registered" 错误

  • 验证类型在两端都使用相同的 ID/名称注册
  • 检查类型名称是否有拼写错误或大小写差异

"Type mismatch" 错误

数据损坏或意外值

  • 验证两端都使用 Language.XLANG 模式
  • 确保两端具有兼容的 Fory 版本

另请参阅

相关主题