65.9K
CodeProject 正在变化。 阅读更多。
Home

ESB - 更简单的总线 - 一个简单高效的集成总线

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2021年5月21日

CPOL

9分钟阅读

viewsIcon

5952

downloadIcon

110

以及为什么 ESB 不应该有 API 合约!

引言

几年前,我写了一系列关于企业服务总线(ESB)不同方面的文章,主要目的是说明实现一个可行的ESB框架需要多么少的投入。从那时起,我花时间反思了集成工程师和工程团队的工作流程,我发现有必要重新审视ESB,并提出一个更简单的解决方案,使ESB成为一个更明显、更优的选择。

插件方案

但是,当你的ERP、CRM或其他需要与另一个系统集成的业务系统已经提供了原生的插件来解决这个问题时,为什么要费力去开发ESB和自定义代码呢?嗯,一般来说,你应该只将开发资源投入到能够区分你公司的业务流程上。通常,例如,你的订单到现金流程会与其他类似的公司的相同流程非常相似,通常这就是插件可能存在的地方。如果你的目的有现成的插件,我认为你应该考虑使用它。

然而(!),你需要意识到,当你选择使用插件时,你就放弃了对该功能的所有影响:你将不知道——也无法决定插件何时被调用,也无法自定义受其影响的数据。根据我的经验,即使是最标准的集成,在最常见的业务系统之间,最终也并非完全标准,或者换句话说:安装插件后不久,你的流程就会出现一个你根本无法适应的需求。你会发现,此时此刻改变你的策略将既昂贵又令人沮丧。

ESB解决方案

我相信为了集成目的,应该建立一个简单而高效的ESB。ESB的一般特征在我之前的文章中已经描述过,这里足以说明,ESB是发布-订阅模式的实现:来自一个系统的消息被发布到ESB,一个或多个系统订阅这些消息并在消息出现时进行消费。ESB成为一个交换机,大多数业务系统的事件数据都可以在实时获得。

开发过程

在大多数情况下,集成是由一个系统需要了解另一个系统中发生的事件驱动的:ERP需要了解电子商务系统中的订单、发票和信用卡支付;电子商务系统需要了解ERP中注册的电汇付款等。因此,调查工作通常从找出源系统中可能触发的事件以及可以提供哪些数据开始。通常,这实际上是容易的部分。处理源系统的工程师或集成工程师将确定这一点,然后系统将调用ESB提供的某个URL,并附带一个包含数据的消息,该数据通常采用XML或json格式。

API契约

这个URL/端点/接收位置——或者你想怎么称呼它——对于绝大多数系统来说,都应该是一个Web服务,并且本文的重点是,这个Web服务应该是完全通用的,能够接收任何内容的任何消息!有了这个,你将不需要再创建一个API——至少对于ESB来说不需要,你只需要授权它来添加新的发布者。

开发者用于与业务系统交互的公共API,例如当你需要将ESB上发布的数据插入到某个目标系统时使用的API,都有非常严格的契约,详细描述了所需的负载,例如使用Swagger或OAS,并且接收到的负载会根据这些规范进行验证。这是很有道理的:这些API是读取、插入、更新和删除特定系统中对象的手段——方法的规则是清晰的,并且应该存在契约。

然而,在ESB的上下文中,情况则恰恰相反:其思想应该是数据在没有特定目的的情况下被发布。在实践中,第一个需要数据的集成可能看起来是特定的目的,但概念上,它只是另一个消费者或订阅者。数据将被潜在的许多不同的订阅者消费。在ESB的上下文中,源定义契约,并且这里的关键点是ESB的端点,即API,将没有(也不应该有)契约。

无契约API设计

这是一个创建异常精简高效的端点的机会,供系统在ESB上发布数据——源系统几乎可以创建它想要的URL,只要它命中你的服务域。

在我一篇关于路由的文章中,我建议了一个反映源系统和消息类型的端点URL结构。重点是创建一个接收Web服务,它只接收消息并将其发布到消息队列。源系统将用于确定消息要发布的交换(exchange),消息类型将作为路由键(routing key)添加到消息中。在提到的文章中,我建议使用RabbitMQ,我仍然支持这个建议,尽管这个模式当然也可以与其他消息代理一起工作,如Kafka或Google Pubsub。

然而,源系统不必是URL的一部分,因为它可以根据其凭证来识别。路由应该由识别出的系统和URL中的路由键决定——一个最好能表达消息包含什么类型数据的键。因此,URL路径可以简化为只包含路由键,即https:///order

关联ID

由于应用程序/集成与ESB和其下方的服务方法具有复合架构,强烈建议在将消息发布到消息代理之前,在消息头中添加一个关联ID。这将提供一种将日志负载和错误消息关联起来的方法,随着集成的复杂性增加,这个需求变得越来越重要。我在一篇早期的文章中也讨论过这一点——我认为它仍然值得一读。

请注意,在我发表那篇文章时,我还没有认为NodeJS是生产就绪的。现在我这么认为。

ESB设计

如上所述,ESB实现了发布-订阅模式。在本文中,我建议通过一个通用的Web服务进行发布。路由由RabbitMQ的交换和队列处理,订阅则由监听队列上消息的消费者完成。组件可以这样说明:

由于本文只关注发布部分,包含的代码项目仅包含一个Web服务来提供讨论的端点。ESB模式的美妙之处在于,消费者可以用任何语言或工具创建,如Node、Go、Kotlin、C#、Mulesoft——基本上任何可以从队列读取、进行必要转换并传递到目标的东西。传递甚至可以由其自己的组件处理,如图所示,这样每个目标系统可能只有一个组件负责传递,因此只有一个组件拥有与该特定系统交互的相关凭证。这将符合三层设计,让我们的接收服务成为体验(x-)层,消费者成为转换或处理(p-)层,以及传递服务成为系统(s-)层。

分层设计在这个Mulesoft的白皮书中有更详细的讨论。你会发现我的建议在这里与x-层的作用有很大不同,因为我的观点是,ESB的前端本身并不是一个API。

下图说明了组件之间如何相互依赖。示例表明系统A发布的数据被系统C消费,系统B发布的数据被系统C和D消费。

Web服务要求

Web服务的要求可以列出如下:

  • 它应该对接收到的负载具有惰性(但你可以选择转换为例如json,以便所有发布的 are 消息都是相同的格式)。
  • 它应该要求身份验证,并根据发布用户进行路由。
  • 它不需要重新部署即可支持新的发布者。

这些是包含的Web服务应用程序实现的要求。要接入一个新的源系统,只需要为它创建凭证。有了这些凭证,源系统就可以准备好向你的ESB发布消息了!

代码

附加的项目是一个Node应用程序。它使用RabbitMQ作为消息代理,并将用户(和交换配置)存储在Mongodb中。我建议你在本地运行它们,例如在Docker中。

项目仅包含源文件,因此你需要创建一个Express应用程序,将项目解压到其中,并在启动之前运行`npm install`。

用户定义在`users`集合中。单个文档仅包含名称、密码和交换(用于RabbitMQ)——我已经提供了一个我使用的`testusers`的导出。

要让应用程序执行一些操作,你需要将一个包含正文的消息POST到类似于上面提到的URL(`https://:3000/order`),并将`content-type`设置为`application/json`或`application/xml`。

请注意,如果你成功POST了一个消息,应用程序将创建配置好的交换,但由于没有队列绑定到它,消息将不会在任何地方找到。创建一个队列,将其绑定到交换(例如,路由键为`#`),然后发送另一个消息。当然,这可以通过代码解决,通过在创建交换时始终创建一个转储队列(dump queue),但这确实是一个设计选择。

历史

  • 2021年5月21日:初始版本
© . All rights reserved.