4. 企业服务总线(ESB)模式:核心功能与落地实践
说到 ESB,很多刚接触 SOA 的同学第一反应就是「这不就是个消息中转站吗?」
嗯,这么说也不算错,但太简单了。
我在早期做项目时,也以为 ESB 就是个管道,把 A 的数据搬到 B 就行。
结果呢?系统上线后,路由规则乱成一团,数据格式天天打架,业务方天天找上门。
后来我才真正理解:ESB 的核心不是「搬数据」,而是「治理集成」。
4.1 ESB 的核心功能:路由、转换、编排
ESB 的三大核心能力,我习惯用一个比喻来记:路由是导航,转换是翻译,编排是导演。
4.1.1 路由(Routing)
路由解决的是「消息该去哪」的问题。
别小看这个功能,真实场景远比你想的复杂。
- 基于内容的路由:根据消息体里的字段值,决定转发到哪个服务。比如订单金额大于 1 万走审批流程,小于 1 万直接发货。
- 基于上下文的路由:根据调用方身份、来源系统、时间等上下文信息路由。比如内部系统调用走内网,外部合作伙伴走专线。
- 动态路由:运行时根据注册中心的服务状态动态调整目标。服务挂了?自动切换到备用节点。
4.1.2 转换(Transformation)
转换解决的是「消息长什么样」的问题。
你想想看,一个企业里可能有几十个系统,每个系统都有自己的数据格式。
CRM 用 XML,ERP 用 JSON,老系统用固定长度的文本文件。
没有 ESB 做转换,这些系统之间就得两两对接,N 个系统需要 N×(N-1)/2 个接口——这谁受得了?
常见的转换模式:
- 格式转换:XML ↔ JSON ↔ CSV ↔ 二进制
- 协议转换:HTTP ↔ JMS ↔ FTP ↔ Socket
- 数据映射:字段名、数据类型、枚举值的映射。比如 A 系统的「性别:0/1」映射到 B 系统的「性别:M/F」
- 数据增强:在消息流转过程中补充数据,比如根据用户 ID 查询姓名并追加到消息里
4.1.3 编排(Orchestration)
编排解决的是「多个服务怎么配合」的问题。
一个业务流程往往需要调用多个服务,而且有先后顺序、有分支、有异常处理。
ESB 的编排能力,就是把这些服务调用串成一个完整的业务流程。
举个例子,一个下单流程:
- 调用库存服务扣减库存
- 调用支付服务发起支付
- 调用物流服务创建配送单
- 如果支付失败,回滚库存
这些步骤如果用代码写在业务系统里,耦合度太高。用 ESB 编排,流程变更只需要改配置,不用改代码。
4.2 ESB 与 API 网关的对比
这个问题我经常被问到:「有了 API 网关,还要 ESB 干嘛?」
说实话,这两个东西确实有重叠,但定位完全不同。
| 对比维度 | ESB | API 网关 |
|---|---|---|
| 核心定位 | 企业集成总线,连接异构系统 | API 统一入口,管理南北向流量 |
| 协议支持 | 多协议(JMS、FTP、SOAP、REST…) | 以 HTTP/HTTPS 为主 |
| 消息转换 | 强,支持复杂数据映射和格式转换 | 弱,通常只做简单的请求/响应转换 |
| 服务编排 | 支持,可编排多个服务形成业务流程 | 不支持或很弱 |
| 路由能力 | 内容路由、上下文路由、动态路由 | 基于 URL 路径的静态路由为主 |
| 治理能力 | 服务注册、监控、SLA 管理 | 限流、熔断、鉴权、日志 |
| 典型场景 | 系统间集成、数据同步、业务流程自动化 | 微服务 API 暴露、移动端/前端接入 |
说白了,ESB 管的是东西向流量(系统与系统之间),API 网关管的是南北向流量(外部到内部)。
我见过不少团队把 API 网关当 ESB 用,结果路由规则写了几百行,性能一塌糊涂。也见过把 ESB 当网关用,结果暴露了太多内部服务细节,安全风险很大。
4.3 轻量级 ESB 实践
传统 ESB(比如 IBM WebSphere ESB、Oracle SOA Suite)太重了,部署复杂、学习成本高、运行起来吃资源。
现在大家更倾向于轻量级 ESB,说白了就是「只做 ESB 最核心的事,其他能省则省」。
4.3.1 轻量级 ESB 的选型原则
- 基于消息中间件:用 RabbitMQ、Kafka、RocketMQ 作为传输层,ESB 只做路由和转换
- 配置驱动:路由和转换规则用 DSL 或配置文件定义,不用写代码
- 容器化部署:支持 Docker/K8s,可以快速扩缩容
- 可观测性:集成 Prometheus、Grafana,能监控每条消息的流转情况
4.3.2 一个简单的轻量级 ESB 示例
我用 Camel + RabbitMQ 搭过一个轻量级 ESB,核心代码就几十行:
// 定义路由:从 RabbitMQ 接收订单消息
from("rabbitmq:exchange:order?routingKey=order.create")
// 消息转换:XML 转 JSON
.unmarshal().jacksonxml(Order.class)
.marshal().json(JsonLibrary.Jackson)
// 内容路由:根据金额分流
.choice()
.when(simple("${body.amount} > 10000"))
.to("rabbitmq:exchange:order.approve?routingKey=approval")
.otherwise()
.to("rabbitmq:exchange:order.ship?routingKey=shipping")
.end();
你看,路由、转换、编排都在一个 DSL 里完成了。部署的时候打个 jar 包,扔到 Docker 里就跑起来了。
4.3.3 轻量级 ESB 的典型架构
下面这张图是我常用的轻量级 ESB 架构,你可以参考:
4.3.4 落地时的几个坑
最后分享几个我踩过的坑:
- 别在 ESB 里做事务:ESB 本质是异步消息系统,强事务会让性能急剧下降。用补偿机制代替分布式事务。
- 注意消息幂等性:消息可能重复投递,消费端要做好幂等处理。我遇到过因为没做幂等,库存扣了两次的惨案。
- 监控比功能更重要:ESB 是集成枢纽,一旦出问题影响面很大。一定要有消息轨迹追踪和告警机制。
- 别过度编排:编排逻辑太复杂,ESB 会变成单点故障。复杂的业务流程建议用工作流引擎,ESB 只做服务调用。