面向哲思的编程与架构
笔记哲思阅读动态搜索RSS 订阅
切换到深色模式
搜索
RSS 订阅
切换到深色模式
© 2026 Vic Chen. All rights reserved.CC BY-NC-ND 4.0
← 笔记
多语言序列化框架的探索与实践(一)

多语言序列化框架的探索与实践(一)

2023年9月13日4,02112分钟

以 Fury 为例,深入剖析多语言序列化框架所涉及的底层核心原理和技术,并结合核心金融工程服务进行探索和实践。


目录
  • TL;DR
  • 1. 背景
  • 1.1. 静态序列化框架
  • 1.2. 动态序列化框架
  • 2. Fury 序列化框架
  • 2.1. 整体架构
  • 2.2. 代码结构
  • 3. 小结
  • 4. 参考文献
目录
  • TL;DR
  • 1. 背景
  • 1.1. 静态序列化框架
  • 1.2. 动态序列化框架
  • 2. Fury 序列化框架
  • 2.1. 整体架构
  • 2.2. 代码结构
  • 3. 小结
  • 4. 参考文献
目录
  1. TL;DR
  2. 1. 背景
  3. 1.1. 静态序列化框架
  4. 1.2. 动态序列化框架
  5. 2. Fury 序列化框架
  6. 2.1. 整体架构
  7. 2.2. 代码结构
  8. 3. 小结
  9. 4. 参考文献
Fury序列化
相关文章
  • 01
    给网站加朗读功能(二):声音复刻而不是通用音色2026/07
  • 02
    AI 搜索内核(一):从字符串到符号2026/07
  • 03
    Claude Code 规模化实践(一):如何在大型代码库中工作2026/07
← 上一篇VaR 计算性能千倍提升:一次国产替换实践
下一篇 →高频交易系统核心构建技术的探索与实践(一)

评论

© 2026 Vic Chen · 面向哲思的编程与架构CC BY-NC-ND 4.0

TL;DR

序列化已经成为分布式系统中频繁涉及的技术之一。在多个服务或组件之间,我们往往需要通过序列化对对象(Object)进行跨进程、跨节点、甚至跨语言的传输和持久化。因此,序列化框架的性能以及异构语言的支持能力将直接影响整个系统的性能和研发效率。

ℹ

本系列将以 Fury (Chen 注:2024 年初,该项目更名为 Fory(GitHub: apache/fory),并已捐献给 Apache 基金会,现以 Apache Fory(孵化中)形式运营。文中所有 「Fury」均指现在的 Apache Fory,核心架构和 API 设计未发生根本性变化。)为例,深入剖析多语言序列化框架所涉及的底层核心原理和技术,并结合核心金融工程服务,进行探索和实践。系列文章主要分为以下几个部分:

  • 第一篇:主要介绍序列化框架的背景,Fury 的整体架构和代码结构
  • 第二篇:核心技术(零拷贝、JIT 动态加速、存储)的原理,及其在 Fury 和其他框架中的实现
  • 第三篇:序列化框架的性能测试,以及 Fury 在核心服务的应用

1. 背景

序列化(Serialization)是将对象转化为字节流的过程,而反序列化(Deserialization)则是将字节流重新转化为对象的过程。传统的序列化方案在效率和安全性上存在一定的局限性,而在金融领域中,海量的交易数据、用户信息等需要进行高效的序列化和反序列化处理,因此我们迫切地需要一个高性能、高扩展性、安全可靠的新一代序列化解决方案。

1.1. 静态序列化框架

通常,在分布式系统中进行数据交换时,我们需要将数据以某种格式进行编码,并在传输后解码为原始数据。静态序列化框架就是用来实现这个过程的。这些框架通常会提供一个接口和一个编解码器,使得使用者可以通过定义数据结构(类似于 C 语言的 struct)将其转换为指定的二进制格式。在传输数据之前,该二进制数据会被序列化并压缩。同样,我们需要在接收端对该二进制数据进行解码,还原为原始数据。

Protobuf、FlatBuffer 和 Thrift 都是流行的静态序列化框架,它们都能完成以上过程,但存在如下一些差异:

  • 编码方案不同:Protobuf 采用可变长度整型编码(varint)、长度前缀编码或固定长度编码;Thrift 使用可变长度整型编码或固定长度编码;FlatBuffer 使用位域编码。
  • IDL(Interface Description Language)不同:Protobuf 和 Thrift 采用 IDL 来定义数据结构和服务接口;FlatBuffer 则不需要 IDL,开发者使用 C++ 代码生成器手动编写数据结构。FlatBuffer 使用 .fbs schema 文件定义数据结构,通过 flatc 编译器生成各语言代码,风格上与 Protobuf IDL 有所不同。FlatBuffer 则不需要 IDL,开发者使用 C++ 代码生成器手动编写数据结构。(Chen 注:FlatBuffer 有自己的 schema 定义语言(`.fbs` 文件),并非不需要 schema,只是没有采用 Protobuf/Thrift 风格的 IDL 命名。开发者仍需先编写 `.fbs` 文件,再通过 flatc 编译器生成各语言代码。)
  • 序列化与反序列化方式:Protobuf、Thrift 和 FlatBuffer 均支持二进制协议和 JSON 格式,但序列化方式各不相同。Protobuf 和 Thrift 序列化成一个字节数组,并进行压缩(可选),而 FlatBuffer 则序列化为一个平面的字节数组。
  • 应用场景不同:使用 Protobuf 或 Thrift 跨语言通信时比较方便,因为它们支持多种语言(Java、C++、Python 等);而 FlatBuffer 主要用于 C++ 中,提供最大的性能和最小的内存开销。

1.2. 动态序列化框架

与静态序列化框架相对应的是动态序列化框架,它是一种能在运行时(Runtime)将对象转换为字节流并进行传输或持久化存储的工具。两者最大的区别在于,动态序列化框架不需要显式地定义数据结构,换言之,它不需要对 Schema 进行编译,而是根据对象的实际类型和数据进行编码和解码。

以下是几个常见的动态序列化框架:

  1. JDK 序列化:Java 标准库提供的一种基本的动态序列化框架。它可以将 Java 对象序列化为字节数组,并支持对象图的完全序列化。JDK 序列化简单易用,无需额外引入依赖,但其序列化结果较大、性能较低且不跨语言。
  2. Kryo:一个快速高效的 Java 序列化库,旨在替代 JDK 序列化。它通过提供紧凑的序列化格式和自定义序列化策略来提高性能。Kryo 支持跨语言,可以与其他语言的序列化框架配合使用,同时也支持循环引用、复杂对象图等。Kryo 是纯 Java 序列化框架,不支持跨语言,但在 JVM 内性能出色,支持循环引用、复杂对象图等。Kryo 支持跨语言,可以与其他语言的序列化框架配合使用,同时也支持循环引用、复杂对象图等。(Chen 注:Kryo 官方并不支持跨语言序列化,它是纯 Java 框架,序列化格式与其他语言不兼容。「可以与其他语言框架配合使用」的说法存在误导。Kryo 的优势在于同一 JVM 内的高性能序列化,支持循环引用和复杂对象图是准确的。)
  3. Fst(Fast-Serialization):一个高性能的 Java 序列化框架,专注于提供快速的序列化和反序列化速度。Fst 通过利用字节码生成、缓存和特殊的压缩算法来实现高性能。它支持跨语言,可以与其他语言的序列化框架进行交互。
  4. Hessian:一种轻量级的二进制 RPC 协议,也是一种动态序列化框架。它支持跨语言,并且在多种语言(如 Java、C#、Python)中都有相应的实现。Hessian 序列化结果较为紧凑,传输效率较高,但相对于其他框架,性能可能稍逊一些。
  5. Pickle:Python 标准库提供的一种动态序列化框架,用于将 Python 对象转换为字节流。Pickle 支持几乎所有 Python 数据类型的序列化,包括自定义类和函数。它的序列化结果可以在不同的 Python 解释器之间进行传输和反序列化。

选择动态序列化框架时需要考虑以下因素:跨语言支持、性能要求、序列化结果体积、可扩展性(是否支持复杂对象图、循环引用等特性)。


2. Fury 序列化框架

Fury 序列化框架是为大规模分布式系统和高性能应用场景而设计的。该框架利用了先进的序列化算法和优化策略,旨在提供快速、高效、安全的数据序列化和反序列化能力。与传统的序列化框架相比,Fury 具有以下几个显著特点:

  1. 高性能:通过优化序列化和反序列化算法,如先进的即时编译(JIT)和零拷贝(Zero-Copy)等,实现了卓越的性能表现。
  2. 扩展性强:框架提供了灵活的跨语言传输,可以根据具体应用场景进行定制化开发。
  3. 安全可靠:框架支持数据压缩和校验功能,提供了更加可靠的数据完整性保护。

金融业务具有高并发、大数据量、高安全性的特性,而 Fury 框架能够在保证数据完整性和安全性的前提下,提供快速的数据传输和处理能力,并且支持多种数据格式和数据类型的序列化,因此 Fury 框架能够很好地满足复杂金融业务的需求。

2.1. 整体架构

图 1. Fury 序列化框架的整体架构
图 1. Fury 序列化框架的整体架构

Fury 是一个基于 JIT 动态编译的多语言原生序列化框架,支持 Java/Python/Golang/C++ 等多种语言,提供全自动的对象多语言/跨语言序列化能力:

  • 提供统一的多语言序列化核心功能;
  • 使用高度优化的序列化原语;
  • 支持 Zero-Copy 序列化,支持 Out-of-band 序列化协议,以及堆外内存读写;
  • 基于 JIT 动态编译技术,在运行时异步多线程自动生成序列化代码以优化性能,包括方法内联、代码缓存和消除死代码等,从而减少虚方法调用、条件分支、哈希查找、元数据写入和内存读写等操作,相较于其他序列化框架,性能提升高达 170 倍相较于 JDK 原生序列化,性能提升在官方测试中高达 170 倍(与 Kryo 等已优化框架相比提升幅度更小,需结合实际场景评估)相较于其他序列化框架,性能提升高达 170 倍(Chen 注:「170 倍」来自 Fury 官方基准测试,是与 JDK 原生序列化对比的峰值数字,在特定数据结构下成立。与 Kryo、Fst 等已优化的框架相比,实际提升幅度通常在数倍到十几倍之间,具体取决于对象结构和数据规模。);
  • 支持多种协议,既综合了动态序列化的灵活性和易用性,也具备静态序列化的跨语言能力。

对于 Java 序列化:

  • Fury 可以无缝替代 JDK/Kryo/Hessian,无需修改任何代码,但性能提升高达 170 倍,极大地提升了高性能场景下 RPC 调用、数据传输和对象持久化的效率;
  • 完全兼容 JDK 序列化,并原生支持 JDK 的自定义序列化方法(如 writeObject/readObject/writeReplace/readResolve/readObjectNoData)。

关于跨语言对象图序列化:

  • Fury 实现了自动序列化任意对象的多语言/跨语言能力,无需创建 IDL 文件、手动编译模式生成代码或将对象转换为中间格式;
  • 支持多语言/跨语言自动序列化共享引用和循环引用,无需担心数据冗余或递归错误;
  • 支持对象类型的多态性,可以同时序列化多个子类型对象。

行存序列化:

  • Fury 提供了缓存友好的二进制随机访问行存格式,支持跳过序列化和部分序列化,非常适用于高性能计算和大规模数据传输场景;
  • 支持与 Arrow 列存格式的自动互相转换。
图 2. Fury 行存序列化
图 2. Fury 行存序列化

Fury 针对 Java 语言实现了一套运行时代码生成(Runtime Codegen)框架,利用对象类型的泛型信息进行类型推断,并根据这些信息构建描述序列化代码逻辑的表达式树(Expression Tree)。在运行时,根据表达式树生成高效的 Java 代码,并通过 Janino 编译器将其编译成字节码。这些生成的代码可以加载到用户或者 Fury 创建的 ClassLoader。最后,通过 Java 的 JIT 编译器将代码编译为高效的汇编代码。整个过程可以描述为图 3 的流程图。

图 3. Fury JIT 动态编译加速
图 3. Fury JIT 动态编译加速

为了充分利用 JVM 的性能,Fury 还实现了一套优化器。该优化器将大方法递归拆分为小方法,确保 Fury 生成的所有代码都可以被编译和内联,从而尽可能压榨 JVM 的性能。

通过在运行时动态生成高效的序列化代码,并经过优化器的处理,Fury 能够减少虚方法调用、条件分支、哈希查找、元数据写入和内存读写等操作,大幅加速了序列化的性能。

2.2. 代码结构

根据目录结构,可以对 Fury 项目进行如下简要分析:

模块说明
annotation用于注解的类和接口
builder构建器模式相关类
codegen代码生成相关的类
collection常用集合类型和工具类
exception自定义异常类
io文件读写等输入输出操作
memory内存管理相关类和功能
pool对象池,用于重复使用对象以提高性能
resolver解析相关类
serializer对象序列化和反序列化
type类型处理和转换
util工具类和辅助方法
Fury核心类,主要功能入口
FuryBuilder构建 Fury 对象的构建器类

Fury Java object graph serialization 示例:

初始化 ThreadSafeFury 并序列化对象
ThreadSafeFury fury = Fury.builder().withLanguage(Language.JAVA)
        // 允许反序列化未知类型的对象,更灵活
        // 但如果类包含恶意代码,则可能不安全
        .requireClassRegistration(false)
        .buildThreadSafeFury();
 
byte[] bytes = fury.serialize(object);
fury.deserialize(bytes);

buildThreadLocalFury 方法解析:

buildThreadLocalFury
public ThreadLocalFury buildThreadLocalFury() {
    this.finish();
    ClassLoader loader = this.classLoader;
    this.classLoader = null;
    ThreadLocalFury threadSafeFury = new ThreadLocalFury((classLoader) -> {
        return Fury.newFury(this, classLoader);
    });
    threadSafeFury.setClassLoader(loader);
    return threadSafeFury;
}

执行过程:

  1. this.finish() — 为 ClassLoader 加载的类提供初始化和后续处理操作。
  2. 获取并清空 classLoader,防止内存泄漏。
  3. 创建 ThreadLocalFury 实例,通过 lambda 表达式在每个线程中创建独立的 Fury 对象副本。
  4. 设置 ClassLoader 并返回实例。

序列化核心流程:

serialize 方法
public byte[] serialize(Object obj) {
    MemoryBuffer buffer = (MemoryBuffer)this.bufferLocal.get();
    buffer.writerIndex(0);
    ((LoaderBinding)this.bindingThreadLocal.get()).get().serialize(buffer, obj);
    return buffer.getBytes(0, buffer.writerIndex());
}

执行过程:从线程本地变量获取 MemoryBuffer,重置写索引,通过 LoaderBinding 调用序列化方法,最后返回字节数组。

serialize(MemoryBuffer, Object, BufferCallback) 方法负责写入对象的元数据标志位(是否为 null、字节序、语言类型、是否有 callback),然后根据语言类型选择 Java 序列化路径(writeRef)或跨语言路径(xserializeInternal)。

writeRef 方法:

writeRef
public void writeRef(MemoryBuffer buffer, Object obj) {
    if (!this.refResolver.writeRefOrNull(buffer, obj)) {
        ClassInfo classInfo = this.classResolver.getOrUpdateClassInfo(obj.getClass());
        this.classResolver.writeClass(buffer, classInfo);
        this.writeData(buffer, classInfo, obj);
    }
}

先通过 refResolver 判断对象是否已序列化过(处理共享引用),若未序列化则依次写入类信息和对象数据。

writeClass 方法根据类是否已注册(classId == 0)选择写入完整类名(包名 + 类名)或仅写入 2 字节的 classId,后者效率更高。

writeData 方法通过 switch 按 classId 路由到对应的写入方法,基本类型(Boolean、Byte、Char、Short、Int、Float、Long、Double、String)直接写入,整型和长整型支持可变长度压缩(varint);其他类型递增深度计数并调用对应序列化器的 write 方法。

xserializeInternal 方法用于非 Java 语言的序列化:先写入两个 -1 占位(对象大小和对象池索引),调用 xwriteRef 序列化对象,然后回填实际大小,最后遍历对象池中的 native 对象逐一序列化。


3. 小结

随着金融互联网行业的快速发展,对数据序列化和反序列化技术的要求将不断提升。Fury 序列化框架作为一种高性能、可扩展、安全可靠的解决方案,有着广阔的应用前景和市场需求。 Fury 在金融互联网应用中为金融业务的处理和传输提供了高效、安全的解决方案。随着金融互联网的持续发展,Fury 框架将在金融领域中发挥越来越重要的作用,推动金融行业实现更高效、便捷的数字化转型。

本文介绍了序列化框架的两种范式——静态框架(Protobuf、Thrift、FlatBuffer)和动态框架(JDK、Kryo、Hessian 等)的核心差异,并深入分析了 Fury 的架构和实现。

从源码视角看,Fury 的高性能来自三个核心机制:其一,运行时 JIT 代码生成(避免反射和泛型类型擦除的性能损耗);其二,引用解析器(refResolver)统一处理共享引用和循环引用;其三,基于 classId 的快速路由(writeData 的 switch 分支),绕过了大量元数据查找。

对于金融场景而言,Fury 的价值不只是「快」,更在于它能以最小迁移成本替换现有 JDK/Kryo 方案——无需修改业务代码,且兼容 JDK 自定义序列化协议,这对存量系统的升级改造尤为关键。

随着金融互联网行业的快速发展,对数据序列化和反序列化技术的要求将不断提升。Fury 序列化框架作为一种高性能、可扩展、安全可靠的解决方案,有着广阔的应用前景和市场需求。 Fury 在金融互联网应用中为金融业务的处理和传输提供了高效、安全的解决方案。随着金融互联网的持续发展,Fury 框架将在金融领域中发挥越来越重要的作用,推动金融行业实现更高效、便捷的数字化转型。(Chen 注:原文小结为通用套话,未提炼文章核心洞察,修订版本重写。)

4. 参考文献

  • Fury GitHub
  • Protocol Buffers
  • FlatBuffers Java Guide
  • Apache Thrift
  • Fury 序列化框架介绍 - 阿里云开发者社区
  • Java 序列化框架性能对比 - 掘金
  • fast-serialization (Fst)
  • Hessian 协议 - Dubbo 文档
  • Kryo 序列化 - 掘金
Language多语言支持
ThreadLocalFury线程局部变量版本
ThreadSafeFury线程安全版本