< 返回版块

Philip Z 发表于 2026-07-21 11:43

Tags:teaql,ai-coding

Java 处理业务复杂度,Rust 承担运行时关键性。不同的微服务可以拥有不同的领域模型,但工程团队仍然可以拥有一致的开发体验。

Rust 的优点已经不需要反复证明。

内存安全、零成本抽象、可预测的运行时表现、优秀的工具链,以及对并发错误更强的编译期约束,使它非常适合构建基础设施、高性能网络服务和可靠性要求较高的软件。Rust 也长期保持着很高的开发者认可度。

但如果把视线从开源项目和基础设施软件转向金融、支付、供应链、制造、保险等大型商业系统,就会看到另一个现实:

Rust 的实际覆盖面仍然远小于 Java。

这并不意味着 Rust 不适合企业软件,也不意味着企业技术负责人没有看到 Rust 的优势。更重要的原因是,大型商业系统选择技术时,考虑的从来不只是语言本身。

他们还需要考虑:

  • 已经运行多年的 Java 系统;
  • Spring、数据库、消息队列、工作流和第三方 SDK 生态;
  • 数十人甚至数百人的现有工程团队;
  • 招聘、培训、运维和故障排查体系;
  • 监管、审计和长期维护要求;
  • 无数已经被生产事故验证过的异常路径。

因此,我逐渐形成了一个判断:

Rust 进入大型商业软件的最好方式,可能不是替代 Java,而是进入 Java 已经建立起来的生态。

不是要求企业先建立一个完整的 Rust 世界,而是在现有系统中找到真正适合 Rust 的边界,让它逐步成为系统中不可替代的一部分。


一、Rust 面对的主要障碍,不一定是技术能力

讨论 Rust 的企业落地时,我们经常从语言优点开始:

  • Rust 没有 GC;
  • 内存占用更低;
  • 性能更可预测;
  • 编译器可以发现更多错误;
  • 容器镜像可以更小;
  • 启动速度更快。

这些都很重要,但它们并不能自动转化为企业采用。

大型商业系统的真实决策公式更接近:

技术收益
- 迁移成本
- 人才成本
- 生态缺口
- 组织认知成本
- 生产风险
= 是否值得采用

一项技术即使在局部指标上更优秀,只要进入成本过高,企业仍然会选择继续优化现有系统。

Java 的优势也不仅仅是“历史悠久”。它已经形成了一整套企业级生产能力:

  • Spring Boot 与 Spring Cloud;
  • 成熟的 ORM、事务、消息和缓存生态;
  • 数据库、支付、身份认证和企业中间件 SDK;
  • 大规模团队协作经验;
  • 监控、诊断、性能分析和运维体系;
  • 大量理解 Java 的架构师、工程师和服务商。

所以,对 Rust 社区而言,真正值得回答的问题不是:

Rust 是否比 Java 更先进?

而是:

如何让企业在不放弃现有 Java 资产的情况下,低风险地获得 Rust 的价值?


二、最困难的路线:先说服企业重写

一种常见的技术叙事是:

现有 Java 系统
    ↓
发现性能或可靠性问题
    ↓
用 Rust 重写核心系统
    ↓
获得更高性能和更低资源占用

这条路线在少数边界清晰的基础设施组件上可能成立,但对大型商业系统往往并不现实。

一个运行多年的支付或金融系统,其价值不仅存在于设计文档中,还存在于大量看似不优雅的代码里:

  • 某个银行接口偶发返回的特殊状态;
  • 某个国家节假日造成的清算例外;
  • 某类商户需要额外的合规审核;
  • 某个历史版本遗留下来的兼容逻辑;
  • 某次事故后增加的保护分支;
  • 某项监管要求形成的审计流程。

这些知识很难通过一次重写完整迁移。

重写还会带来新的风险:

  • 原有异常场景是否被完整覆盖?
  • 新旧系统并行期间如何对账?
  • Rust 团队是否理解全部业务语义?
  • 原有运维工具能否继续使用?
  • 出现事故时,组织是否有足够的人能够排查?

因此,Rust 的企业落地不应该建立在“推倒重来”之上。

企业真正需要的不是一次语言革命,而是一条可以逐步验证的采用路径。


三、更现实的入口:从微服务边界进入

微服务提供了一个非常现实的机会。

只要服务边界足够清晰,企业就不必为整个系统选择唯一语言。不同微服务可以根据自己的工作负载、变化速度和运行时要求选择不同技术。

以一个支付平台为例,它可能同时包含两类性质差异很大的服务。

1. 业务与集成复杂度较高的服务

例如:

Java Services
├── Merchant Onboarding
├── KYC / KYB
├── Compliance Workflow
├── Account Maintenance
├── Document Processing
├── Partner Integration
├── Reconciliation Investigation
└── Operations & Administration

这类服务的主要难点通常是:

  • 流程长;
  • 状态多;
  • 外部集成多;
  • 异常分支复杂;
  • 业务变化频繁;
  • 需要运营人员介入;
  • 需要大量企业级组件。

Java 的成熟生态在这里非常有价值。

我们可以把这一类复杂度称为:

Business Complexity:业务复杂度。

2. 运行时关键性较高的服务

例如:

Rust Services
├── Client-facing Payment API
├── Payment Execution
├── Payment Routing
├── Real-time Pricing
├── Transaction Validation
├── Idempotency Control
├── Gateway Callback Processing
└── High-volume Event Processing

这类服务往往具有不同特征:

  • 面向最终用户;
  • 请求量大或者流量波动明显;
  • 延迟直接影响支付体验;
  • 需要快速横向扩容;
  • 对单实例内存和部署密度敏感;
  • 服务边界相对清晰;
  • 关注 P99、P99.9 等尾延迟指标。

我们可以把这一类问题称为:

Runtime Criticality:运行时关键性。

这不是按照“重要”和“不重要”来划分系统。

商户入驻和 KYC 并不比支付执行不重要;合规流程甚至可能决定企业能否继续经营。两者只是面对不同类型的复杂度。

因此,更准确的表达是:

Java 吸收业务与集成复杂度,Rust 承担运行时关键性。


四、Rust 的价值不应被简化为“比 Java 快”

在这种架构中使用 Rust,不能只依赖一句“Rust 性能更好”。

现代 JVM 已经非常成熟。经过良好设计和调优的 Java 服务,同样可以获得优秀的吞吐和延迟表现。许多所谓的性能问题,真正的瓶颈也可能位于:

  • 数据库;
  • 网络调用;
  • 锁竞争;
  • 消息系统;
  • 不合理的数据模型;
  • 过多的服务跳转;
  • 缺乏缓存;
  • 错误的容量规划。

把这些问题简单归因于 GC,通常是不严谨的。

Rust 更适合作为候选方案的情况是:

  1. 服务边界已经比较稳定;
  2. 运行时成本确实是主要矛盾;
  3. 已经完成数据库、网络和架构层面的基本优化;
  4. 对尾延迟、内存密度、冷启动或弹性扩容有明确目标;
  5. 能通过基准测试和生产指标验证收益。

例如,一个面向用户的支付执行服务,在活动期间可能需要迅速从少量实例扩容到大量实例。此时,Rust 的潜在收益可能不只是平均响应时间,而是:

  • 更低的单实例资源需求;
  • 更高的容器部署密度;
  • 更快的启动速度;
  • 更可预测的运行时行为;
  • 在高并发下更稳定的尾延迟;
  • 编译期内存安全带来的可靠性收益。

但这些收益必须通过真实工作负载验证,而不能只通过一个脱离业务的 Hello World benchmark 宣布。

Rust 应该通过可测量的运行时价值进入系统,而不是通过语言偏好进入系统。


五、不要为了双栈而共享一个“超级领域模型”

当 Java 和 Rust 同时出现在一个系统中时,一个很自然的想法是:

能否让两边共享完全相同的业务模型?

在单体系统、SDK 或跨平台客户端中,这种方式可能有价值。但在微服务场景下,强行共享完整领域模型往往不是好主意。

例如,商户服务可能拥有:

Merchant
BeneficialOwner
KycCase
ComplianceReview
MerchantDocument

支付执行服务可能拥有:

PaymentIntent
Quote
PaymentRoute
Execution
GatewayCallback
PaymentStatus

它们属于不同的 bounded context,拥有独立的:

  • 数据库;
  • 生命周期;
  • 部署节奏;
  • 内部状态;
  • 领域约束。

为了追求“Java 和 Rust 一致”而合并这些模型,反而可能重新制造强耦合。

在微服务边界上,真正需要共享的是有限且稳定的契约,例如:

MerchantApproved
MerchantSuspended
PaymentRequested
PaymentAccepted
PaymentCompleted
PaymentFailed

因此,更合适的原则是:

不同微服务各自建模,只在 API 与事件边界共享必要语义。

也就是说:

Different service models.
Clear interface contracts.

六、双技术栈真正的风险,是工程方式碎片化

即使服务边界设计合理,引入 Rust 仍然会产生一个组织问题。

传统情况下,Java 团队和 Rust 团队很容易分别形成两套体系:

Java Team
├── 一套查询方式
├── 一套审计方案
├── 一套错误模型
├── 一套测试规范
├── 一套项目结构
└── 一套 AI Coding 指南

Rust Team
├── 另一套查询方式
├── 另一套审计方案
├── 另一套错误模型
├── 另一套测试规范
├── 另一套项目结构
└── 另一套 AI Coding 指南

局部性能可能提高了,但整个组织的认知成本也提高了:

  • Java 工程师很难阅读 Rust 服务;
  • Rust 工程师需要重新理解 Java 项目的工程约定;
  • 架构规范在两个团队中逐渐分叉;
  • 审计和可观测性语义不一致;
  • AI Agent 需要学习两套完全不同的开发方式;
  • 一项跨服务需求更容易在交接处丢失语义。

因此,多语言架构真正需要解决的,不只是“服务之间如何通信”,还包括:

如何在保留语言选择的同时,避免工程体验和组织认知被撕裂?

我认为这里应该追求的不是:

One shared domain model.

而是:

One shared way of building.


七、TeaQL 的尝试:不同模型,相同的工程语法

TeaQL 同时提供 Java 和 Rust 实现。

我们的目标不是让 Java 服务和 Rust 服务共享一个巨大的模型,也不是让所有代码逐行一致,而是让两边拥有相似的工程表达方式。

例如,在 Java 中,一个带业务目的的查询可能是:

Q.payments()
    .comment("Find payments requiring reconciliation")
    .withStatusIsCompleted()
    .withReconciliationStatusIsPending()
    .purpose("prepare reconciliation batch")
    .executeForList(ctx);

在 Rust 中,可以采用相似的表达:

Q::payments()
    .comment("Find payments requiring reconciliation")
    .with_status_is_completed()
    .with_reconciliation_status_is_pending()
    .purpose("prepare reconciliation batch")
    .execute_for_list(ctx)
    .await?;

这两段代码:

  • 不一定属于同一个微服务;
  • 不一定使用相同的领域模型;
  • 不一定访问相同的数据库;
  • 不需要共享同一个运行时。

但工程师和 AI 看到的是相似的结构:

选择业务对象
→ 表达业务条件
→ 说明操作目的
→ 在上下文中执行
→ 形成审计与追踪信息

TeaQL 希望统一的是:

  • 业务意图的表达;
  • 查询和数据访问的基本形态;
  • purposecommentaudit 等工程语义;
  • 状态与约束的类型化表达;
  • 代码生成方式;
  • 测试与质量门禁;
  • AI Coding 所需的指南和上下文;
  • 服务开发的整体工作模式。

这可以概括为:

不同的运行时,相同的工程体验。

或者:

TeaQL 不统一微服务内部模型,而是统一开发微服务的方式。


八、一个可运行的 Java + Rust 支付参考实现

为了验证这种思路,我们实现了一个小型参考项目:

polyglot-payment-with-teaql

项目地址:

https://github.com/teaql/polyglot-payment-with-teaql

它包含两个独立微服务。

Java Merchant Service

基于 Spring Cloud 与 TeaQL Java,负责:

  • 商户注册;
  • KYC 信息;
  • 审批与暂停;
  • 商户状态变更;
  • Transactional Outbox;
  • 服务发现和状态同步。

Rust Payment Service

基于 Axum 与 TeaQL Rust,负责:

  • 面向用户的支付创建;
  • 支付回调;
  • 状态查询;
  • 商户状态本地缓存;
  • 高并发请求处理;
  • 独立扩容与部署。

整体结构如下:

+---------------------------------------------------+
|              Java Merchant Service                |
| Merchant Registry / KYC / Approval / Integration  |
+-------------------------+-------------------------+
                          |
              Merchant status contract
                          |
                          v
+-------------------------+-------------------------+
|               Rust Payment Service                |
| Checkout / Payment / Callback / High Concurrency  |
+-------------------------+-------------------------+

两个服务:

  • 各自拥有领域模型;
  • 各自拥有数据库;
  • 各自决定部署生命周期;
  • 只在服务边界交换必要信息;
  • 使用相似的 TeaQL 工程方式。

Java 侧使用 merchant_db,Rust 侧使用 payment_db。Rust 支付服务会维护本地的商户可用状态,避免每一次支付请求都同步调用 Java 服务,从而减少用户请求关键路径上的跨服务依赖。

这个项目并不是一个完整的支付产品,也不是要证明所有支付服务都应该使用 Rust。

它想验证的是一个更有限、也更现实的命题:

Java 和 Rust 可以根据不同工作负载共同构建大型商业系统,而不必让工程组织陷入完全割裂的双技术栈。


九、Rust 进入 Java 企业系统的四步路径

基于这个思路,我认为更稳妥的落地方式可以分成四步。

第一步:寻找运行时价值明确的边界

不要从“哪个 Java 服务看起来最旧”开始,而要从指标开始:

  • 哪条链路的 P99/P99.9 最重要?
  • 哪个服务的流量波动最大?
  • 哪个服务扩容最频繁?
  • 哪个服务的内存成本最高?
  • 哪个服务的边界最清晰?
  • 哪个服务失败后最容易隔离和回滚?

理想的第一个 Rust 服务应该:

  • 边界清晰;
  • 业务变化相对可控;
  • 能够独立部署;
  • 可以逐步切流;
  • 收益可以量化。

第二步:先统一契约,不急于统一实现

在编码之前,先定义:

  • API schema;
  • 事件格式;
  • 幂等语义;
  • 错误码;
  • 超时和重试规则;
  • 版本兼容策略;
  • 审计字段;
  • 可观测性要求。

Java 与 Rust 不需要共享内部对象,但必须对边界语义形成一致理解。

第三步:建立统一的工程约定

至少需要统一:

  • Trace ID 和业务上下文;
  • 日志字段;
  • 审计语义;
  • 错误分类;
  • 指标命名;
  • 测试分层;
  • 安全检查;
  • API 兼容策略;
  • 发布和回滚流程。

只有这样,Rust 才是现有工程体系中的新成员,而不是一个孤立的技术岛。

第四步:用生产指标决定是否扩大

可以比较:

  • P50、P95、P99、P99.9 延迟;
  • 单实例吞吐;
  • CPU 与内存消耗;
  • 容器启动时间;
  • 扩容响应时间;
  • 错误率;
  • 事故恢复时间;
  • 开发周期;
  • 缺陷密度;
  • 团队维护成本。

只有当综合收益成立,才继续把 Rust 扩展到更多服务。


十、哪些情况下不应该使用 Rust?

主张 Java + Rust 混合,并不意味着应该主动增加技术栈。

以下场景通常不适合作为 Rust 的第一入口:

1. 业务规则每天变化

如果一个服务的主要问题是产品需求频繁变化、第三方 SDK 很多、流程持续调整,那么迁移到 Rust 不一定能改善核心矛盾。

2. 性能瓶颈主要在数据库

如果请求的大部分时间都花在慢 SQL 或远程服务上,更换语言通常不会带来预期收益。

3. 团队缺乏维护能力

如果只有一名工程师能理解 Rust 服务,那么它可能成为新的组织风险。

4. 服务边界尚未稳定

在领域边界仍然剧烈变化时过早拆分,可能同时承担微服务复杂度和新语言复杂度。

5. 只是为了“技术先进”

缺少可衡量目标的 Rust 项目,很容易在内部变成昂贵的技术展示。

因此,我更倾向于一个克制的原则:

Java by default, Rust by justification.

Java 可以继续作为大量企业业务服务的默认选择;Rust 在存在明确运行时价值的地方进入。


十一、AI 编程让统一工程方式变得更重要

在 AI Coding 时代,多语言开发的门槛似乎正在降低。

AI 可以帮助工程师:

  • 生成 Rust 代码;
  • 理解所有权和生命周期错误;
  • 编写 Java 与 Rust 之间的接口;
  • 生成测试;
  • 完成重复性迁移。

但 AI 同样会放大工程碎片化。

如果每个服务都有不同的:

  • API 风格;
  • 数据访问方式;
  • 审计方法;
  • 错误约定;
  • 目录结构;
  • 验收标准;

AI 就必须反复阅读大量上下文,并且更容易产生局部正确、整体不一致的代码。

因此,统一工程体验的价值在 AI 时代反而更大:

Language choice remains flexible
            +
Engineering grammar remains stable
            =
Lower cognitive cost for humans and AI

多语言并不可怕。

真正危险的是:

每增加一种语言,就增加一套完全不同的软件工程体系。


结语

Rust 要进入金融和大型商业软件,未必需要先证明自己能够取代 Java。

更现实的路径,是承认 Java 已经建立起来的企业生态,在其中找到真正适合 Rust 的服务边界:

  • 面向最终用户;
  • 性能与资源效率敏感;
  • 需要快速弹性扩容;
  • 运行时可预测性很重要;
  • 边界足够清晰;
  • 收益能够被验证。

Java 和 Rust 不需要共享一个代码库,也不需要共享一个巨大的领域模型。

它们需要的是:

  • 清晰的服务契约;
  • 一致的业务语义;
  • 相近的工程表达;
  • 统一的审计与可观测性;
  • 可重复的开发、测试和交付方式。

最终,我们希望实现的不是:

Java or Rust

而是:

Java for business complexity.
Rust for runtime criticality.
One consistent engineering experience.

Rust 不必推倒 Java 的世界。它可以先成为这个世界里不可替代的一部分。


参考资料

  1. TeaQL Java + Rust 支付微服务参考实现
    https://github.com/teaql/polyglot-payment-with-teaql

  2. 2025 Stack Overflow Developer Survey — Technology
    https://survey.stackoverflow.co/2025/technology

  3. 2025 State of Rust Survey Results
    https://blog.rust-lang.org/2026/03/02/2025-State-Of-Rust-Survey-results/

  4. GitHub Octoverse 2025
    https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/

评论区

写评论

还没有评论

1 共 0 条评论, 1 页