用 Rust 打造更快未来:银行如何利用 JDK 22 的巧妙互操作技巧尝试 Rust

了解 JDK 22 的外部函数和内存 API 如何让银行在不中断 Java 单体系统的情况下集成 Rust 以实现高性能交易。更快、更便宜、更环保。

用 Rust 打造更快未来:银行如何利用 JDK 22 的巧妙互操作技巧尝试 Rust

想象一下,您的 Java 交易平台运行良好,突然 JVM 决定进行一次“清理”,触发垃圾回收暂停。数百万英镑的潜在交易在系统喘息时悄然溜走。我们都见过事后分析报告:撮合引擎在数据馈送峰值期间冻结 800 毫秒;风险引擎因为一次完整 GC 选择在此时清理而错过保证金催缴窗口。银行多年来一直忍受这种问题,像抱着安慰毯的幼儿一样紧紧抓住他们的单体 Java 系统。但现在 JDK 22 提供了一种巧妙的方式,让您可以在不引发全面革命或触发合规危机的情况下引入 Rust。

老一套的借口越来越站不住脚。当竞争对手悄然实现更好的执行时间和更低的云成本时,“它能用”不再是一种策略。监管机构对运营韧性的提问越来越尖锐。人才池正在转变。而您支付的硬件大部分时间都在等待下一个 GC 周期结束。

秘密武器:外部函数和内存 API

两个互锁的金属机制通过零拷贝光通道连接
JDK 22 FFM 作为遗留核心与原生模块之间的直通发光桥梁。

神奇的成分是 JDK 22 中根据 JEP 454 最终确定的外部函数和内存 API(Foreign Function and Memory API)。忘掉老旧脆弱的 JNI 舞蹈,它带来的指针运算噩梦、手动引用计数以及让每次安全审查都变成多周 ordeal 的安全隐患。这个新工具包让您的 Java 代码可以直接调用由 Rust 共享对象编译的原生库,实现零拷贝数据交换和真正有意义的编译时安全检查。

这就像给您老旧的 JVM 提供一套电动工具,而无需拆掉布线。您保留有效的部分(周围的 Scala 或 Kotlin 业务逻辑、报告管道、监管钩子),只加速那些真正带来痛苦的部分。订单撮合、风险引擎或实时定价等热点路径留在 Rust 中,而其他部分则继续愉快地运行。多家一级机构已经这样做了,将 Rust 微服务与 Java 单体系统并行部署,并在受监管的变更窗口内实现零停机切换。

在底层,您获得正确的内存区域来为您管理生命周期、结构化的 MemoryLayout 描述(可干净映射到 Rust 结构体),以及一个 Linker,它将原生符号转换为可像任何其他 Java 方法一样调用的 MethodHandle。如果您不想自己编写描述符,jextract 工具甚至可以从 C 头文件生成大部分样板代码。不用再在到期日下午 2 点与原生指针玩俄罗斯轮盘。

以下是最小的演示。首先是 Rust 端,编译为 cdylib:

// Cargo.toml
// [lib]
// crate-type = ["cdylib"]

#[no_mangle]
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
    // 激动人心的示例,但它证明了互操作有效
    // 而 JVM 不会出现它的小状况
    a + b
}

按常规方式构建:

cargo build --release
# 产生 target/release/libtrading_rust.so(或 .dylib / .dll)

现在是使用最终 FFM API 的 Java 端:

import java.lang.foreign.*;
import static java.lang.foreign.ValueLayout.*;

public class RustInteropDemo {
    public static void main(String[] args) throws Throwable {
        // 加载 Rust 共享对象(根据您的环境调整路径)
        System.load("/path/to/target/release/libtrading_rust.so");

        Linker linker = Linker.nativeLinker();
        SymbolLookup lookup = SymbolLookup.loaderLookup();

        MemorySegment addSymbol = lookup.find("rust_add")
            .orElseThrow(() -> new RuntimeException("Symbol not found"));

        FunctionDescriptor descriptor = FunctionDescriptor.of(
            JAVA_INT, JAVA_INT, JAVA_INT
        );

        MethodHandle addHandle = linker.downcallHandle(addSymbol, descriptor);

        int result = (int) addHandle.invokeExact(40, 2);
        System.out.println("Rust says 40 + 2 = " + result);
    }
}

就是这样。没有 JNI 胶水代码。没有脆弱的 Makefile。两边的编译器都会让您保持诚实。

为了展示对交易工作负载真正重要的零拷贝部分,这里是一个稍有趣的示例。一个 Rust 函数,计算从 Java 传入的数组的简单 VWAP,而无需复制数据:

#[no_mangle]
pub extern "C" fn calculate_vwap(
    prices: *const f64,
    volumes: *const f64,
    len: usize,
) -> f64 {
    // 在生产环境中您应添加适当验证或使用更安全的包装器。
    // 这是互操作边界;像对待潜艇气闸一样对待它。
    let mut sum_pv = 0.0;
    let mut sum_v = 0.0;

    unsafe {
        for i in 0..len {
            let p = *prices.add(i);
            let v = *volumes.add(i);
            sum_pv += p * v;
            sum_v += v;
        }
    }

    if sum_v > 0.0 { sum_pv / sum_v } else { 0.0 }
}

在 Java 端,您使用 Arena 分配堆外内存,填充它,然后传递地址。Rust 直接读取。没有 GC 暂停。没有先将整个市场数据快照复制到堆中。

真正重要的性能

回报是立竿见影且令人满意的。Rust 实现通常提供相当于 Java 系统的两到五倍吞吐量,即使在市场真正疯狂时也能提供严格的尾延迟保证。高频交易部门尤其喜欢确定性的亚微秒响应,因为持续 50 微秒的优势可以通过更精准的订单执行转化为可观的额外收入。

不再有抖动使您的风险价值模型或压力测试失效。基准测试甚至显示 Rust FIX 引擎和限价订单簿在吞吐量上比优化的 C++ 高出 12% 至 15%,同时保持 99.9 百分位延迟表现良好。差异来自零成本抽象、不与借用检查器冲突的无锁数据结构,以及完全没有一个 GC 线程可以随时决定停止世界的存在。

您的性能分析工具不再在成交量激增时像圣诞树一样闪烁。过去在后台运行的风险计算现在能在下一个市场数据 tick 到达前完成。这才是会体现在损益表而不仅仅是幻灯片中的改进。

运行成本更低(也更环保)

这不仅仅是原始速度的问题。那些 Rust 二进制文件比 Java 等效版本少消耗 30% 至 60% 的 CPU 和内存。这意味着您可以缩小云实例,在相同工作负载下将基础设施支出削减 40% 或更多。

节省会 cascade 传递:更少的服务器、您迁移的部分不再有 JVM 许可开销、更低的电费让 ESG 群体感到欣慰。能耗曲线更接近 C 而非 Java,因此您可以更早淘汰旧设备、整合工作负载,并在不费力的情况下满足央行可持续性要求。真实金融科技迁移的回报期不到九个月,纯粹由这些基础设施收益驱动。

当下一次运营韧性问卷到来时,您可以指向硬数据,而不是“我们增加了更多堆内存并祈祷最好”。

“Rust 太难了”的神话已死

最好的部分是,关于 Rust 对企业团队来说太难的老借口已经被 AI 悄然终结。现代大型语言模型工具可以吐出通过 Clippy 和编译器的生产级 Rust,而多代理工作流则在闭环中处理架构样板、内存安全和优化。

高级工程师可以停止与借用检查器搏斗,回到领域逻辑。试点报告显示,从想法到安全可部署二进制文件的速度提高了三到四倍。过去的高风险高投入转型现在变成了高回报的下午项目。借用检查器成为您非常有主见但最终正确的同事,它拒绝让您将 use-after-free 错误发布到生产环境中。

那些仍然声称它太难的团队,通常是那些还没有用当前工具尝试过的人。尝试过的人不会再回头。

随处部署——边缘、设备或两者兼顾

一旦 Rust 入门,部署选项就会变得更有趣。将相同代码编译为 WASM 并放入 Cloudflare Workers 或 Fastly,实现欺诈检测或外汇定价的亚 10 毫秒边缘执行,与集中式设置相比可将往返时间缩短高达 70%。

或者直接推送到设备:交易终端、移动应用、支付硬件或 IoT 网关,本地处理让敏感数据驻留,并大幅降低延迟。跨平台一致性意味着核心库无论是在数据中心服务器还是交易员的笔记本电脑上运行,行为都完全相同。加速您撮合引擎的同一个 crate,也可以运行交易室平板电脑上的盘前检查。

是时候行动了

对于仍依赖传统 Java 或较旧 C++ 堆栈的机构来说,不断上升的成本、不可预测的暂停、监管压力和人才战正成为真正的拖累。保持现状不再是安全的选择。它是昂贵的那个。

为您最延迟敏感或成本最高的负载委托一个有针对性的概念验证。让 AI 增强的团队完成繁重工作。衡量吞吐量、尾延迟、实例大小和功耗的差异。合规文件比全面重写更轻,因为您不是在替换系统。您只是在加速那些带来痛苦的部分。

拥抱这种混合路径不是什么疯狂的架构赌博。它是一个战略上的必然选择,能提供卓越的性能、更低的成本、更好的可持续性和长期韧性,同时让您保留已经支付的 Java 投资。

您的股东会注意到利润率的改善。监管机构会看到更好的尾延迟表现。交易大厅将不再抱怨系统又变慢了。而您将成为那个终于不再假装 JVM 会自己修复的人。

这不是一场革命。只是使用正确的工具完成工作,同时保持系统正常运行。