SOA 面向服务架构

2006 年,某国有银行启动了一个大型 IT 改造项目:把分散在各省份的核心系统整合成全国统一的平台。项目预算 3 个亿,历时 5 年。

技术方案是当时最流行的 SOA(Service-Oriented Architecture,面向服务架构)。IBM 和 Oracle 作为总集成商,引入了企业服务总线(ESB),把所有系统连接在一起。

2011 年项目上线。但上线后的第一年,光是解决 ESB 性能问题就花了 8 个月。项目后来成为业界「SOA 失败」的经典案例。

SOA 为什么会失败?是技术不对,还是方法错了?

SOA 的核心思想

SOA 诞生于 1990 年代末期,在 2000 年代初达到巅峰。它的核心理念是:把业务功能封装成可复用的服务,通过标准化的接口和服务总线实现系统间通信

服务重用

SOA 之前,企业信息系统是烟囱式的:财务系统、人力资源系统、供应链系统各自独立,数据不互通。如果要在新系统中用到某个功能,往往是复制代码而不是复用。

SOA 的思路是:把通用的业务能力抽取成「服务」,任何系统需要时通过接口调用。

传统方式(复制代码):
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   财务系统   │  │   HR 系统   │  │  供应链系统  │
│  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │
│  │税务计算│←┘  │  │税务计算│←┘  │  │  │税务计算│←┘  │
│  └───────┘     │  └───────┘     │  └───────┘     │
└─────────────┘  └─────────────┘  └─────────────┘
问题:同一段逻辑被复制了 3 份,改一个要改 3 个地方

SOA 方式(服务复用):
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   财务系统   │  │   HR 系统   │  │  供应链系统  │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────────────┼────────────────┘

              ┌──────────────────┐
              │    税务计算服务    │
              │   (统一维护)      │
              └──────────────────┘

分布式计算

SOA 把系统从单机进程拆分成多个分布式服务,服务之间通过网络通信。技术上,SOA 主要基于 SOAP 协议(基于 XML 的 Web Service)和 WSDL(Web Service Description Language)来定义和描述服务。

<!-- WSDL 示例:定义一个税务计算服务 -->
<definitions name="TaxService"
    targetNamespace="http://example.com/tax">
    <message name="calculateTaxRequest">
        <part name="amount" type="xsd:decimal"/>
        <part name="taxType" type="xsd:string"/>
    </message>
    <message name="calculateTaxResponse">
        <part name="tax" type="xsd:decimal"/>
    </message>
    <portType name="TaxPortType">
        <operation name="calculateTax">
            <input message="tns:calculateTaxRequest"/>
            <output message="tns:calculateTaxResponse"/>
        </operation>
    </portType>
</definitions>

企业服务总线(ESB)

ESB 是 SOA 的核心组件,它扮演着「服务中介」的角色:所有服务之间的调用不直接通信,而是通过 ESB 路由。

ESB 提供的核心能力:

能力说明
消息路由根据配置把请求路由到目标服务
协议转换把 SOAP 转成 JMS,或者 REST 转成 SOAP
消息转换转换数据格式,比如 XML 转 JSON
服务编排把多个服务调用组合成一个流程
安全认证统一处理认证、授权、加密
┌──────────────────────────────────────────────────────────┐
│                      企业服务总线(ESB)                    │
│                                                          │
│   ┌─────────┐      ┌─────────┐      ┌─────────┐        │
│   │财务服务  │←────→│   ESB   │←────→│ HR 服务  │        │
│   └─────────┘      │         │      └─────────┘        │
│                    │ 路由    │                          │
│   ┌─────────┐      │ 转换    │      ┌─────────┐        │
│   │供应链服务│←────→│ 编排    │←────→│CRM 服务  │        │
│   └─────────┘      │ 监控    │      └─────────┘        │
│                    └─────────┘                          │
└──────────────────────────────────────────────────────────┘

SOA 与单体的核心差异

服务粒度

单体架构的服务粒度是「模块」,模块之间通过方法调用通信。SOA 的服务粒度是「业务能力」,服务之间通过网络协议通信。

粒度对比:

单体模块:
public class OrderService {
    public void createOrder(Order order) {
        // 调用本地方法
        User user = userService.getUser(order.getUserId());
        Product product = productService.getProduct(order.getProductId());
        // ...
    }
}

SOA 服务(通过 ESB 远程调用):
// OrderService 调用 UserService
// 实际调用路径:OrderService → ESB → UserService
// 每次调用都是一次网络请求

通信协议

单体使用进程内调用(Java 里的方法调用),几乎没有序列化开销。SOA 使用网络协议(SOAP over HTTP、JMS、REST),每次调用都有网络延迟和序列化开销。

数据管理

单体共享同一个数据库实例,通过事务保证一致性。SOA 中每个服务可能有自己独立的数据库,服务间通过消息或 API 调用协调数据一致性。

ESB 的作用与局限

ESB 解决了什么问题

异构系统集成:很多企业有几十年前的老系统,协议和数据格式各不相同。ESB 通过协议转换和消息转换,让这些系统可以互相通信。

服务复用:把通用能力抽取成服务,通过 ESB 暴露给其他系统复用。

集中监控:所有服务调用都经过 ESB,可以统一监控、审计、限流。

ESB 的局限

中心化瓶颈:所有流量都经过 ESB,ESB 成为新的单点。一旦 ESB 性能出问题,所有服务都受影响。

ESB 性能瓶颈场景:

某银行 ESB 高峰期每秒处理 5000 笔交易
当业务增长到每秒 10000 笔时:
1. ESB 需要扩容,但 ESB 服务器比普通应用服务器贵 10 倍
2. 单点扩容无法解决问题,需要集群
3. 集群带来了新的复杂度:负载均衡、故障转移
4. 每次扩容都需要重新测试所有集成点

总线成为故障中心:ESB 的监控和告警非常重要,但当 ESB 出现故障时,排查链路非常复杂。请求经过 ESB 后路由到哪个服务、超时时间是多少、是否有消息堆积,都需要专门的知识才能排查。

过度设计:很多 SOA 项目死于过度设计。为了追求「完美」的服务抽象,花了半年设计服务边界,结果业务逻辑变了,之前的设计全部推翻。

SOA 失败的常见原因

过度设计

SOA 鼓励服务抽象,但很多团队把这个理念发挥过头了。简单的一个 CRUD 操作,被包装成复杂的服务调用链。

过度设计的例子:

需求:查询用户列表

# 正确做法(直接调用)
GET /users

# 过度设计的 SOA 方式
1. API 网关接收请求
2. 路由到用户聚合服务
3. 用户聚合服务调用用户基础信息服务(获取基本信息)
4. 用户聚合服务调用用户权限服务(获取权限信息)
5. 用户聚合服务调用用户偏好服务(获取偏好设置)
6. 聚合结果返回

结果:一个简单的查询,变成了 4 次网络调用,延迟从 5ms 变成 50ms

服务粒度不合理

服务划分有两个极端:服务太粗(和单体没区别)和服务太细(过度拆分导致调用链路爆炸)。

SOA 早期普遍存在「服务太粗」的问题。很多 SOA 项目只是把单体拆成了几个大服务,粒度和单体模块差不多,没有享受到服务化的好处。

后来微服务吸取了这个教训,强调「服务要小」,但又带来了新问题。

ESB 滥用

很多企业把 ESB 当成万能解决方案:性能问题靠 ESB 解决,安全问题靠 ESB 解决,可靠性问题也靠 ESB 解决。结果 ESB 承担了太多职责,变成一个臃肿的中心化组件。

SOA 的教训是:架构要去中心化,不要把所有鸡蛋放在一个篮子里。这一点后来被微服务彻底贯彻。

技术背景:CORBA、Web Services、SOAP

CORBA

CORBA(Common Object Request Broker Architecture)是 1990 年代的对象中间件标准。它允许不同语言、不同平台的对象互相通信。

CORBA 的失败在于:标准太复杂,实现之间不兼容,安全模型不完善。在企业市场的竞争中,被微软的 DCOM 和后来Sun的 RMI 击败。

Web Services

Web Services 是 2000 年代初的企业集成标准,包括:

  • SOAP:Simple Object Access Protocol,基于 XML 的消息协议
  • WSDL:Web Services Description Language,描述服务接口
  • UDDI:Universal Description, Discovery and Integration,服务注册发现

Web Services 比 CORBA 更简单,获得了更广泛的支持。但 SOAP 消息的 XML 序列化开销大,解析速度慢,在性能敏感的场景下表现不佳。

REST 的崛起

2000 年代末,REST 风格的 API 开始流行。相比 SOAP,REST 更简单、性能更好,逐渐成为 Web API 的主流。

SOAP vs REST 对比:

SOAP 请求:
POST /TaxService HTTP/1.1
Content-Type: text/xml; charset=utf-8

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <calculateTax>
            <amount>1000</amount>
            <taxType>VAT</taxType>
        </calculateTax>
    </soap:Body>
</soap:Envelope>

REST 请求:
POST /api/tax/calculate
Content-Type: application/json

{
    "amount": 1000,
    "taxType": "VAT"
}

REST 的优势:消息体积小、解析快、易于调试

SOA 与微服务的本质差异

很多人以为微服务就是「把 SOA 的名字换了一下」。实际上,两者有本质差异:

维度SOA微服务
设计目标企业级集成,异构系统互通敏捷开发,快速交付
服务粒度粗粒度,业务组件细粒度,单一职责
通信协议SOAP/WSDL,企业总线REST/gRPC,轻量级
数据管理共享数据库,ESB 编排独立数据库,去中心化
部署模式中心化,企业级应用服务器独立部署,容器化
治理理念统一标准,集中管控团队自治,去中心化

关键差异:数据管理

SOA 的 ESB 编排往往意味着服务间共享数据,或者通过 ESB 做数据聚合。这导致服务之间紧耦合,修改一个服务可能影响其他服务。

微服务强调「每个服务有自己的数据库」,服务之间通过 API 通信,数据完全隔离。这种设计让服务可以独立修改、独立部署。

数据管理差异:

SOA(共享数据):
┌─────────────┐
│     ESB     │
└──────┬──────┘

┌──────────────────────────────┐
│        共享数据库             │
│  [用户表] [订单表] [商品表]    │
└──────────────────────────────┘
问题:多个服务读写同一套表,修改表结构需要协调多个团队

微服务(独立数据):
┌──────────┐  ┌──────────┐  ┌──────────┐
│用户服务   │  │订单服务   │  │商品服务   │
│ ┌──────┐ │  │ ┌──────┐ │  │ ┌──────┐ │
│ │用户库 │ │  │ │订单库 │ │  │ │商品库 │ │
│ └──────┘ │  │ └──────┘ │  │ └──────┘ │
└──────────┘  └──────────┘  └──────────┘
优势:每个服务独立管理数据,可以独立演进

关键差异:治理理念

SOA 强调「企业级标准」,所有服务必须遵循统一的技术规范、命名规范、安全规范。这保证了系统的一致性,但也限制了灵活性。

微服务强调「团队自治」,每个团队可以自主选择技术栈、发布节奏、数据库类型。只要服务对外暴露的 API 不变,内部实现可以自由改变。

真实案例:某银行 ESB 项目的教训

以下案例来自 2011 年某国有银行的真实项目(已脱敏):

背景:银行有 20 多个省份的核心系统,数据格式、通信协议各异。项目目标是整合成全国统一的平台。

方案:IBM 提供 ESB 平台,Oracle 提供数据库,整合所有系统。

问题

  1. 性能瓶颈。ESB 每秒处理 5000 笔交易,而业务高峰需要 20000 笔。扩容 ESB 需要采购更贵的服务器,一台 ESB 服务器的价格是普通应用服务器的 10 倍。

  2. 协议转换复杂。有些老系统用的是专有协议,ESB 支持得不好,导致集成花了大量时间。

  3. 维护成本高。ESB 的配置非常复杂,每次业务变更都需要修改 ESB 配置。维护 ESB 需要专门的团队,人员成本很高。

  4. 故障排查困难。当一笔交易失败时,需要查看 ESB 日志才能知道调用链路。ESB 日志格式不友好,排查一次问题可能需要几天。

教训:银行后来反思,这次项目的失败不是 ESB 技术不对,而是「用 ESB 解决所有问题」的想法本身就是错的。ESB 适合做协议转换和路由,但不适合做高性能的交易处理。

SOA 的适用场景

虽然 SOA 有很多失败案例,但它在某些场景下仍然有价值:

异构系统集成

当企业有大量历史遗留系统,技术栈各异,需要互相通信时,ESB 的协议转换能力非常有用。

强流程编排需求

当业务流程需要跨多个系统、涉及复杂的状态机时,ESB 的服务编排能力可以统一管理流程。

强合规要求

金融、医疗等行业有严格的审计要求。ESB 的集中日志和监控能力可以满足合规需求。

总结

SOA 是企业级架构的第一次大规模实践,它的核心理念(服务复用、标准化接口)影响了后来整个行业。但 SOA 的失败也留下了宝贵的教训:

  • 去中心化:不要把所有能力集中在 ESB,让服务自主管理自己的数据和业务逻辑
  • 服务粒度:服务要足够小,才能独立部署和扩展
  • 轻量级通信:过度设计的协议会增加复杂度,性能也会受影响

微服务诞生于对 SOA 失败的反思,它继承了好理念,抛弃了坏实践。下一节,我们来详细讲解微服务时代。

思考题

问题 1:某企业有 10 个历史遗留系统,用的协议和数据格式都不一样。是否应该引入 ESB 来做集成?

参考答案

需要看具体情况。如果这 10 个系统之间的调用频率很高,而且业务逻辑需要强一致性,可以考虑引入 ESB。但如果大部分系统是「只读」调用,更好的方案是:直接点对点集成,或者用一个简单的 API 网关来统一入口,不要让 ESB 成为中心化瓶颈。另外,也可以考虑逐步迁移到 RESTful API,统一协议后再简化集成架构。

问题 2:SOA 强调服务复用,但很多 SOA 项目最后变成了「服务复制」,如何避免这个问题?

参考答案

服务复用的前提是服务边界划分合理。如果边界划错了,服务就不是复用而是重复建设。几个建议:

  1. 先做领域建模(DDD),确定服务边界后再抽象接口
  2. 服务设计要稳定,避免频繁修改导致消费方需要同步更新
  3. 建立服务治理机制,鼓励复用而不是重新开发
  4. 衡量复用的收益,如果服务改造频率很高,复用成本可能超过收益

问题 3:微服务声称「每个服务独立数据库」,但这会导致数据一致性问题。SOA 的共享数据库是否反而更简单?

参考答案

这是一个权衡问题。共享数据库确实简化了事务一致性,但引入了紧耦合:修改表结构需要协调所有使用方。独立数据库让服务解耦了,但需要引入分布式事务或最终一致性方案。对于强一致性要求的业务(如金融转账),可以采用「共享数据库 + 独立服务」的方式,或者引入 Saga 模式来处理分布式事务。没有绝对的对错,只有场景的适配。