Back to Article

02. Java/SOFA 微服务核心教程

02. Java/SOFA 微服务核心教程

本章进入微服务和 SOFAStack 主线。SOFAStack 官方介绍把它定位为面向金融级分布式架构的中间件集合,包含研发框架、RPC、注册中心、链路追踪、Metrics、分布式事务、服务治理等能力。学习它时不能只看“怎么引依赖”,要同时理解每个组件承担的系统职责。

1. 微服务先问“为什么”,再问“用什么”

微服务是一种架构和组织方式,不是“Spring Cloud + Nacos + Gateway + Feign”的组件堆叠。

适合拆成微服务的信号:

  • 不同业务模块变更频率差异很大。
  • 模块有独立团队负责。
  • 模块有不同的容量、可用性和安全要求。
  • 数据所有权可以清晰切分。
  • 单体已经因为构建、部署、协作、扩容变得明显低效。

不适合急着拆的信号:

  • 业务边界还不稳定。
  • 团队规模小,部署和运维能力弱。
  • 数据库表高度耦合。
  • 只是为了简历或技术新鲜感。
  • 一个服务挂了整个链路还是挂,拆分没有带来隔离收益。

V2EX 的微服务路线讨论中,有回复提醒“先理解微服务解决什么问题,有什么优缺点”。InfoQ 的 Sock Shop 文章也提到,演示应用后来成为参考应用,但服务合并也可能比想象中更困难。这两类资料都指向同一条经验:服务边界应该谨慎演进。

2. 金融领域的参考业务域

后续示例使用一个简化的金融支付/账户系统:

  • customer-service:客户信息。
  • account-service:账户、余额、冻结金额。
  • payment-service:支付订单、支付状态机。
  • risk-service:风控检查。
  • ledger-service:账务分录。
  • notification-service:短信、邮件、站内信。
  • reconciliation-service:对账和差错处理。

微服务边界不是 Controller 边界,而是数据和业务能力边界。例如支付订单和账务分录最好不要随意共用数据库表。支付服务可以调用账务服务,但不能直接写账务库。

3. SOFAStack 组件视图

3.1 SOFABoot

SOFABoot 是基于 Spring Boot 的研发框架。官方文档说明它增强了 Spring Boot,并提供 Readiness Check、类隔离、日志空间隔离等能力,还让用户在 Spring Boot 中方便使用 SOFA 中间件。

学习重点:

  • 它不是替代 Spring Boot,而是基于 Spring Boot。
  • SOFABoot 版本与 Spring Boot 版本有对应关系,选版本时必须看兼容矩阵。
  • 对金融系统来说,Readiness Check、类隔离、日志隔离等能力服务于稳定性和运维。

3.2 SOFARPC

SOFARPC 是 Java RPC 框架。它承担服务发布、引用、协议、负载均衡、过滤器、序列化、注册中心集成等职责。

RPC 的关键不是“比 HTTP 快”,而是:

  • 面向接口调用。
  • 客户端代理封装远程调用。
  • 统一治理超时、重试、路由、过滤器。
  • 与注册中心、监控、链路追踪打通。

3.3 SOFARegistry

SOFARegistry 是服务注册中心。官方资料强调它是生产级、低延迟、高可用的服务注册中心,并采用分层架构。学习时要理解三个问题:

  • 服务实例如何注册。
  • 消费者如何订阅服务列表。
  • 实例上下线如何推送到消费者。

生产部署时,官方部署文档区分集成部署和独立部署,并建议生产环境按角色独立部署。学习阶段可以本地最小化,生产设计必须考虑可用性、容量和故障域。

3.4 SOFATracer、Lookout、服务治理

微服务一旦拆开,问题会从“代码调用栈”变成“网络调用链”。你必须知道:

  • 一个用户请求经过哪些服务。
  • 每段耗时多少。
  • 哪个服务返回错误。
  • 是否发生重试。
  • 是否触发熔断和限流。
  • 某个交易单号是否完整走完。

链路追踪和 metrics 不是“运维附加项”,而是微服务可用性的基础。

4. 最小 SOFABoot/SOFARPC 思路

下面是教学级结构,实际版本号要按官方兼容矩阵和公司基线确定。

4.1 接口模块

public interface AccountQueryService {
    AccountSnapshot queryByAccountNo(String accountNo);
}

public record AccountSnapshot(
        String accountNo,
        String currency,
        long availableAmount,
        long frozenAmount
) {}

接口模块通常被 provider 和 consumer 共同依赖。注意:

  • DTO 要稳定。
  • 不要暴露 JPA Entity。
  • 金额用最小货币单位整数或明确的 BigDecimal 规则。
  • 字段增加要兼容旧消费者。

4.2 Provider

@SofaService(interfaceType = AccountQueryService.class)
@Service
public class AccountQueryServiceImpl implements AccountQueryService {
    private final AccountRepository accountRepository;

    public AccountQueryServiceImpl(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    @Override
    public AccountSnapshot queryByAccountNo(String accountNo) {
        Account account = accountRepository.findByAccountNo(accountNo)
                .orElseThrow(() -> new AccountNotFoundException(accountNo));
        return new AccountSnapshot(
                account.getAccountNo(),
                account.getCurrency(),
                account.getAvailableAmount(),
                account.getFrozenAmount()
        );
    }
}

教学重点:

  • @SofaService 表达服务发布。
  • 实现类仍然可以是 Spring Bean。
  • 业务异常要有明确错误码,不要把数据库异常直接抛给消费者。

4.3 Consumer

@Service
public class PaymentApplicationService {
    @SofaReference(interfaceType = AccountQueryService.class)
    private AccountQueryService accountQueryService;

    public PaymentPreview preview(String accountNo) {
        AccountSnapshot account = accountQueryService.queryByAccountNo(accountNo);
        return PaymentPreview.from(account);
    }
}

教学重点:

  • 本地看像接口调用,实际上是远程调用。
  • 必须配置超时。
  • 必须知道异常语义。
  • 必须做降级或失败路径设计。

5. RPC 与 REST 的边界

RPC 适合

  • 内部服务间高频调用。
  • 强类型接口。
  • 统一治理和注册发现。
  • 公司内部有明确 SDK/接口版本管理。

REST/HTTP 适合

  • 对外 API。
  • 前后端交互。
  • 跨组织、跨语言集成。
  • 需要良好 HTTP 语义、缓存、网关、OpenAPI 文档。

金融系统常见模式:

  • 外部请求进入 API Gateway,使用 HTTP/REST。
  • 内部核心服务之间使用 RPC 或 gRPC。
  • 异步事件使用 MQ。
  • 报表、对账、数据分析走批处理或数据管道。

6. Spring Cloud 体系的作用

Spring Cloud 官方文档把它描述为帮助构建分布式系统常见模式的工具集合,包括配置管理、服务发现、熔断、路由等。

常见组件能力:

  • Config:外部化配置。
  • Discovery:服务注册发现。
  • Gateway:入口路由、鉴权、限流、灰度。
  • Circuit Breaker:熔断隔离。
  • OpenFeign:声明式 HTTP 客户端。
  • Bus:配置刷新和事件传播。

Spring Cloud Alibaba 关注:

  • Nacos:服务发现和配置。
  • Sentinel:流控、熔断、系统保护。
  • Seata:分布式事务。
  • RocketMQ:消息。

SOFA 与 Spring Cloud 的关系不是“只能二选一”。InfoQ 对 SOFAStack 双模微服务平台的报道提到兼容 Dubbo、Spring Cloud 等运行环境以及平滑迁移思路。实际企业里常见多技术栈并存:

  • 老系统 Dubbo。
  • 新系统 Spring Cloud Alibaba。
  • 金融核心内部 SOFARPC/SOFARegistry。
  • 边缘 API 使用 Spring Cloud Gateway。

关键是统一治理标准:超时、重试、鉴权、链路追踪、日志、错误码、发布流程不能各搞一套。

7. 服务治理核心能力

7.1 超时

默认无超时是事故隐患。每个远程调用都要有超时预算。

示例:

  • 网关总超时:2s。
  • payment 调 account:300ms。
  • payment 调 risk:500ms。
  • payment 写本地 DB:200ms。
  • 预留重试和返回时间:1s。

超时不是越大越安全。超时过大时,上游线程会被拖住,下游恢复后也可能被积压流量打垮。

7.2 重试

只对明确可重试的失败重试,例如网络抖动、读接口、幂等写接口。不要对非幂等支付扣款随意重试。

重试必须配合:

  • 幂等键。
  • 最大次数。
  • 退避策略。
  • 熔断。
  • 指标。

7.3 熔断

熔断的目的不是“修复下游”,而是保护当前服务和整体链路。risk-service 不可用时,payment-service 可以选择:

  • 拒绝高风险交易。
  • 只允许低风险小额交易。
  • 进入人工审核。
  • 返回稍后重试。

金融场景不能随便“降级为成功”。降级策略必须由业务和风控确认。

7.4 限流

限流位置:

  • 网关按用户、IP、接口限流。
  • 服务内按业务动作限流。
  • 下游客户端按依赖服务限流。
  • MQ 消费端按处理能力限流。

限流返回要可解释:是系统繁忙、额度限制、风控限制,还是重复请求。

7.5 灰度和路由

灰度发布需要考虑:

  • 请求按用户、机构、租户、交易类型路由。
  • 数据库 schema 是否兼容。
  • 新老服务是否能同时消费消息。
  • 回滚后是否能处理新版本写入的数据。

8. 服务接口设计

8.1 错误码

错误码至少区分:

  • 参数错误。
  • 鉴权失败。
  • 授权失败。
  • 业务规则拒绝。
  • 幂等冲突。
  • 下游超时。
  • 系统内部错误。

不要让调用方解析异常字符串。

8.2 幂等键

支付创建接口示例:

public record CreatePaymentCommand(
        String requestId,
        String merchantId,
        String orderNo,
        long amount,
        String currency
) {}

幂等键可由 merchantId + orderNo 或全局 requestId 构成。数据库层应该有唯一约束,不能只靠 Redis。

8.3 版本兼容

接口演进建议:

  • 只增加可选字段。
  • 不改变字段语义。
  • 不复用废弃字段。
  • 删除字段前先观察消费者。
  • 关键接口保留兼容层。

9. 本章练习

练习 1:服务拆分设计

给一个单体电商支付模块,拆出:

  • payment-service
  • account-service
  • risk-service
  • ledger-service
  • notification-service

写清楚每个服务拥有的数据表,哪些调用同步,哪些调用异步,哪些失败要补偿。

练习 2:SOFARPC 接口契约

设计 RiskDecisionService

  • 输入:用户、账户、交易、设备、IP。
  • 输出:通过、拒绝、人工审核、需要二次验证。
  • 规定超时、错误码、降级策略。

练习 3:Spring Cloud Gateway 入口

设计网关规则:

  • /api/payments/** 路由到 payment-service。
  • 添加鉴权 Filter。
  • 添加 requestId。
  • 限制单用户每分钟请求数。
  • 灰度 5% 流量到新版本。

10. 本章检查清单

  • 能解释 SOFAStack、SOFABoot、SOFARPC、SOFARegistry 的关系。
  • 能解释 RPC、REST、MQ 的适用边界。
  • 能说明 Spring Cloud 和 SOFA 并存时需要统一哪些治理标准。
  • 能为一个远程调用设计超时、重试、熔断、错误码。
  • 能解释为什么金融写接口必须有幂等键和数据库唯一约束。