28.2 ESB

一句话

ESB 是 “中心化的集成总线”:路由、协议转换、数据映射、编排都放在中间件里;它把 N×M 的点对点集成压成 N+M,代价是业务逻辑沉淀在中间件,形成难以版本化的黑盒。

ESB 的四项能力

能力 做什么 今天的替代
路由 按内容/规则把消息转给不同后端 API 网关 + 消息队列路由
协议桥接 SOAP ↔ REST ↔ MQ ↔ 文件 适配器/Connector/边车
数据映射 字段级转换(XML/XSLT) 应用内防腐层 + schema 转换
编排 多服务流程(BPEL) 工作流引擎(Temporal/Camunda)

代码:一条典型 ESB 路由

<!-- Camel 风格:内容路由 + 转换 + 重试(示意,未在本机运行) -->
<route>
  <from uri="jms:queue:orders"/>
  <choice>
    <when><simple>${body[type]} == 'REFUND'</simple>
      <to uri="bean:refundService?method=handle"/>
    </when>
    <otherwise>
      <to uri="bean:orderService?method=create"/>
    </otherwise>
  </choice>
  <onException>
    <exception>java.lang.Exception</exception>
    <redeliveryPolicy maximumRedeliveries="3"/>
    <to uri="jms:queue:orders.DLQ"/>
  </onException>
</route>

问题也在这里:路由规则、转换逻辑、重试策略全在 XML 里,它们本质是业务决策,却没有代码评审、单元测试与版本语义。

实测:ESB 式转换的开销(本机跑)

2 万条消息,直接字段映射 vs 走 “序列化 → XML → 解析 → 映射”(模拟总线转换):

方式 耗时 相对
直接字段映射 3.5ms 1×
经 XML 解析转换 101.7ms 29×

这说明 ESB 的成本不只是 “多一跳网络”,还有每次消息都要做序列化/解析。把它放在所有流量的必经之路上,等于给每条消息加 29 倍的 CPU 税;批量场景(如日志/事件流)尤其明显。

# 可复现:直接映射 vs XML 解析映射
direct = [{"orderId": m["id"], "amt": m["amount"]} for m in msgs]
# 对照:每条都做 ET.fromstring + findtext

反模式

反模式 后果
把业务规则写进 ESB 中间件变成 “不可测试的业务层”,改一条规则要重启总线
用 ESB 做 “所有流量的单一入口” 单点 + 全局限流,故障爆炸半径最大
XSLT 做复杂转换 没人能维护,逻辑只存在于运行时
契约与实现脱钩 总线改了映射,调用方毫无感知
不设 DLQ 与重放 一条毒消息堵住整个队列
用 ESB 掩盖后端 “不可集成” 适配层越来越厚,最终无人能改

| 路由规则进代码后仍在 ESB 维护 | 两处定义,改一处就行为不一致 | | 不做转换的单元测试 | 上线才发现字段映射错,且只能靠日志猜 |

三条铁律

🔑 ESB 只做 “连接与转换”,不做业务决策;业务必须回到服务里。

🔑 转换逻辑要能被测试与版本化;XML 里的 if-else 是最贵的债。

🔑 必须有 DLQ + 重放 + 幂等,否则一条坏消息能拖垮全链路。

自检

Q1 为什么 ESB 容易变成 “逻辑黑洞”?

▸ 答案:因为它的配置(路由、映射、重试、编排)算业务逻辑,却不在代码仓库里:没有单测、没有 code review、没有语义化版本,改动只能靠运维点击或部署 XML。时间一长,没人能说清 “这条消息到底被谁改成了什么”。

Q2 ESB 与 API 网关的分工是什么?

▸ 答案:网关面向 “外部调用”(鉴权、限流、路由、协议转换到 HTTP);ESB 面向 “内部集成”(多协议、多系统、消息级转换与编排)。现代做法是用 “网关 + 消息队列 + 工作流引擎” 拆掉 ESB 的三项能力,避免单点与黑盒。

Q3 什么场景还适合用 ESB?

▸ 答案:大量异构遗留系统(SOAP、文件、MQ 混合)、且团队已有成熟的总线运维能力时。它能在短期内统一接入;但必须限制 “只做连接与转换”,并把编排迁到可测试的工作流引擎。

Q4 为什么 “ESB 单点” 比 “服务单点” 更危险?

▸ 答案:因为 ESB 处于所有流量的必经之路:它挂掉或变慢,所有集成一起不可用;而单个服务故障只影响一条链路。它的爆炸半径等于整个企业的集成面。

Q5 迁移出 ESB 的顺序应该是什么?

▸ 答案:先搬 “编排”(到工作流引擎,最容易测),再搬 “转换”(到服务内的防腐层),最后搬 “路由”(到网关/队列)。顺序反了会让中间态更难维护——先动路由会把所有链路同时打散。

Q6 如何判断 ESB 里的某段逻辑该搬走?

▸ 答案:问 “它是否依赖业务语义”:只做协议/格式转换 → 留在连接层;涉及 “金额怎么算、状态怎么流转、谁该收到” → 必须搬到服务里。判据是 “这段逻辑变了,是否需要业务方评审”。

Q7 既然转换这么贵,为什么还保留 ESB?

▸ 答案:因为异构协议(SOAP、文件、MQ)之间的桥接没有更便宜的通用解:要么在总线集中做一次转换,要么在 N 个系统里各写一次适配。ESB 的价值是 “集中 + 可治理”,代价是 “每条消息都付转换成本”;所以它只该处理 “必须跨协议” 的流量,内部同构调用应绕开它。

故障复盘(典型模式,脱敏)

阶段 现象 判断 动作
T+0 一条映射规则上线后,订单金额全部错位 字段映射无单测 回滚规则
T+15m 回滚失败——部分消息已按新规则入库 无重放/补偿 手工修数
T+2h 定位到 XSLT 里一个 substring 边界错 逻辑藏在 XML 迁移到代码
之后 转换逻辑进代码 + 单测 可测试 保留总线做协议桥接

根因:把业务级转换写进 ESB 配置,没有测试与重放。代价:金额错账 4 小时。根治:转换留在代码里(可测),ESB 只做协议桥接;必须有 DLQ 与重放。

关联