Ohhnews

分类导航

$ cd ..
DZone Java原文

云原生Java架构:微服务与Serverless的融合之道

#云原生#java架构#微服务#serverless#kubernetes

过去,构建企业级 Java 系统意味着选择应用服务器、部署单体应用,并进行垂直扩展,直到预算耗尽或数据库不堪重负。在 2026 年,现代 Java 团队被期望能够实现更快的发布速度、更高的韧性,以及在不可预测的工作负载下具备弹性的性价比。这正是云原生 Java 架构旨在实现的目标:构建为变化而生,而不仅仅是为了保证正常运行时间的系统。

但“云原生”并不是“运行在 Kubernetes 上”的代名词。一种真正可扩展的方法是将 Java 微服务(用于领域隔离和独立交付)与 Serverless Java(用于突发或事件驱动的工作负载)相结合,并以 Kubernetes 作为 Java 的操作基底,从而实现一致的部署、韧性和可观测性。

让我们剖析一个实用的架构蓝图、关键的权衡取舍,以及那些能够帮助团队在不过度设计的情况下构建可扩展微服务架构的云原生设计模式。

什么是真正的“云原生 Java 架构”

当一个系统围绕以下核心进行设计时,它才是云原生的:

  • 水平扩展(而非单纯追求“更大的服务器”)
  • 容错性(假设组件总会发生故障)
  • 不可变部署(可重复的发布)
  • 环境一致性(开发 → 测试 → 生产环境一致)
  • 自动化优先的运维(CI/CD、策略即代码)
  • 默认具备可观测性(而非事后添加插件)

对于 Java 而言,这也意味着现代的运行时和打包实践:

  • 容器友好的内存和 GC 调优
  • 快速启动策略(特别是在涉及 Serverless 的情况下)
  • 轻量级、可独立部署的服务

目标不是复杂性,而是运维的可预测性。

何时使用微服务与 Serverless(以及何时不该用)

一个常见的错误是将微服务和 Serverless 视为相互竞争的方法。如果你是有意识地使用它们,它们其实是互补的。

使用 Java 微服务的情况:

  • 拥有以不同速度变化的独特业务领域
  • 多个团队需要独立的发布周期
  • 需要具备持续吞吐量的长驻服务
  • 从领域隔离(故障和安全边界)中受益

使用 Serverless Java 的情况:

  • 工作负载具有突发性或季节性
  • 处理事件(文件上传、流、队列消息)
  • 需要缩容至零以实现成本效益
  • 需要针对自动化任务和胶水逻辑的隔离函数

避免使用微服务的情况:

  • 团队规模较小,且领域并不复杂
  • 无法支撑分布式系统的开销(运维、可观测性、SRE)
  • 缺乏强大的 CI/CD 和自动化成熟度

避免使用 Serverless 的情况:

  • 具有高且稳定的吞吐量及可预测的负载(容器可能更便宜)
  • 无法容忍冷启动且无法缓解该问题
  • 需要大量的本地状态或长连接会话

一个可扩展的云原生 Java 策略通常表现为:核心领域使用微服务,事件驱动边缘和自动化任务使用 Serverless。

参考架构:云原生基础上的微服务 + Serverless

以下是一个可扩展微服务架构的实用蓝图:

核心运行时层(平台)

  • Kubernetes:用于运行长驻 Java 服务,处理自动扩缩容、服务发现、滚动部署和韧性原语
  • 托管的消息代理(Kafka/Pulsar)或队列服务,用于异步工作流
  • 集中式可观测性堆栈(OpenTelemetry + 指标/日志后端)
  • 身份与策略强制执行(OIDC、服务间认证)

应用层(服务与函数)

  • 领域对齐的 Java 微服务(例如:订单、计费、客户、库存)
  • Serverless Java 函数,用于:
    • 事件处理器(文件上传、Webhook 处理)
    • 数据转换与扩充
    • 定时任务(计费对账、报表快照)
    • 自动化任务(修复手册、通知)

数据层(所有权与集成)

  • 每个服务一个数据库(或至少实现模式隔离)
  • 用于跨服务工作流的事件驱动集成
  • 用于报告的读取模型(在合理的情况下采用 CQRS 模式)

平台应当通过模板、护栏和默认配置,让“正确的方法”成为最简单的方法。

Java 中可扩展的云原生设计模式

稳定系统与脆弱系统之间的区别通常取决于模式。以下是对 Java 最有用的云原生设计模式。

A. 绞杀者无花果模式 (Strangler Fig)(用于迁移)

如果你正在现代化遗留的单体应用:

  • 在边缘构建新的服务功能
  • 逐步路由流量
  • 渐进式淘汰遗留模块 这可以避免高风险的“大爆炸式”重写。

B. 每个服务一个数据库(或所有权边界)

你不需要每个服务都使用不同的数据库引擎,但你需要明确所有权:

  • 一个服务拥有对数据集的写入权
  • 其他服务通过 API 或事件消费变更 这减少了耦合并提高了自治性。

C. 事件驱动架构 (EDA)

使用事件实现:

  • 长驻工作流
  • 解耦生产者和消费者
  • 无需紧密协调即可扩展集成 提示:保持事件的业务意义(如“InvoiceGenerated”),而非底层技术细节(如“RowUpdated”)。

D. 断路器 + 超时 + 重试

Java 服务必须假设依赖项会失败。实现:

  • 超时以防止资源耗尽
  • 带有抖动/退避机制的重试(避免惊群效应)
  • 断路器以阻止级联故障
  • 舱壁模式以隔离资源池

E. Saga 模式(分布式事务)

当事务跨越多个服务时:

  • 使用 Saga 来编排或协调步骤
  • 实现补偿逻辑,而不是锁定所有资源
  • 确保幂等性,防止重试破坏状态

F. 背压与限流

在微服务系统中,过载会迅速蔓延。应用:

  • 基于队列的缓冲
  • 入口限流
  • 具备背压感知的消费者

G. GitOps 与不可变部署

将部署视为版本化的状态:

  • 基础设施和应用配置存放在 Git 中
  • 变更经过审查、可追溯且可重复
  • 回滚是可预测的

Kubernetes 上的 Java:核心关注点

当你为平台设计而不是与平台对抗时,在 Kubernetes 上运行 Java 的效果最好。

关键关注点

资源分配与 JVM 调优

  • 有意地设置内存和 CPU 的请求/限制 (requests/limits)
  • 调优堆大小以避免 OOM Kill
  • 监控负载下的 GC 行为
  • 启用容器感知设置(现代 JVM 对此支持良好)

正确的健康检查

  • Liveness(存活探针):用于判断“是否应重启”
  • Readiness(就绪探针):用于判断“是否能接收流量”
  • Startup(启动探针):用于初始化缓慢的服务 配置错误的探针会导致不必要的重启和级联故障。

使用正确的指标进行自动扩缩容

CPU 通常是一个较弱的指标。更好的指标包括:

  • 请求速率
  • 延迟 (p95/p99)
  • 队列深度
  • 自定义应用指标

服务间安全

采用:

  • 服务间 MTLS(如果适用,可使用服务网格)
  • 短效身份令牌
  • 最小权限访问策略

Serverless Java:冷启动、打包与事件策略

如果你承认 Serverless 的局限性,它可以表现得非常出色。

冷启动缓解

  • 保持函数精简
  • 最小化依赖图
  • 谨慎复用连接
  • 考虑预置并发(在需要时)

打包方法

  • 使用轻量级运行时,避免为小型函数使用重量级框架
  • 谨慎共享公共库(平衡复用与臃肿)

事件设计

  • 确保处理器是幂等的
  • 使用关联 ID (Correlation IDs) 进行追踪
  • 安全处理重复交付和重试 Serverless 最适合作为事件驱动层,而不是所有服务的替代品。

可观测性:可扩展系统的非协商条件

如果你想要一个可扩展的云原生 Java 架构,你需要:

  • 跨服务和函数的分布式追踪
  • 带有关联 ID 的结构化日志
  • 服务仪表板(延迟、错误率、饱和度)
  • 反映用户影响的 SLO 和告警 没有可观测性,微服务只会成为调试的负担。

合理的采用路线图

要在不陷入混乱的情况下实现可扩展架构:

  1. 从模块化单体或少量服务开始
  2. 搭建平台和可观测性基础
  3. 提取一个领域并实施 EDA 以进行集成
  4. 在边缘添加 Serverless 函数以处理事件和自动化
  5. 通过模板和护栏实现模式标准化
  6. 根据实际生产经验进行迭代

结语

可扩展的云原生 Java 架构并非盲目采用微服务或 Serverless 就能实现。它是通过将架构选择与工作负载模式、团队结构和运维成熟度相匹配,然后应用经过验证的云原生设计模式并辅以强大的平台护栏来实现的。

在核心领域使用 Java 微服务实现自治,在突发、事件驱动的工作负载中使用 Serverless Java。在 Kubernetes 上构建基础,并内置可观测性、安全性和自动化。这种组合才是现代 Java 团队在不牺牲可靠性的前提下实现快速交付的关键。