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 模式来处理分布式事务。没有绝对的对错,只有场景的适配。