开发人员为何需要企业服务总线?

时间:2022-05-08
本文章向大家介绍开发人员为何需要企业服务总线?,主要内容包括引言、调用服务、同步直接调用、同步代理调用、异步代理调用、其他集成功能、事件通知、开发企业服务总线、服务网关、消息总线、更好的消息总线、同步还是异步?、结束语、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

引言

重要的应用程序很少是单独存在的;如果不能与其他的应用程序一起使用,应用程序将难以发挥很大的作用。面向服务的体系结构往往将应用程序集成在一起,这样它们就可以协同工作并提高工作效率,每个应用程序都分成必须相互集成的各个部分。SOA 模型——服务使用者调用服务提供者——可能看起来相当简单,但是它提出了两个重要的问题:

  1. 使用者如何找到它需要调用的服务的提供者
  2. 使用者如何快速而可靠地调用服务,而网络实际上很慢且不可靠?

对于这两个问题,有一个相当简单的答案,即采用称为企业服务总线 (ESB) 的方法。ESB 处理使用者和提供者之间的所有复杂问题,从而使得服务调用对于两者都比较简单。ESB 不仅使应用程序(或其各个部分)可以更加容易地调用服务,而且还帮助它们转换数据和广播事件通知。ESB 的设计体现了许多已为大家接受的设计模式和标准规范。

本文旨在帮助开发人员理解 ESB 的作用以及应用程序集成的必要部分(包括 SOA)。重点不在于定义或产品,而在于 ESB 实现的功能,这样您就不必自己实现这些功能。它展示了 ESB 可以为您做什么。

调用服务

为了帮助您理解应用程序集成和 SOA,我将从介绍 Web 服务如何工作开始。Web 服务只不过是您可以用来实现服务调用的一种方法。它们甚至可能不是最好的方法,但却是目前可用的最标准的方法,它们能够帮助我形成正在尝试完成的任务的设计。

首先,我必须解释相关术语。Web 服务非常类似过程性编程中的功能:它具有名称、参数和结果。名称就是统一资源标识符 (URI),通过 URI,Web 服务提供者 使 Web 服务可以作为端点使用。Web 服务使用者将端点 URI 作为查找和调用 Web 服务的地址。使用者调用端点时会将请求传送到 Web 服务,请求中包含特定的操作和参数。在执行服务之后,端点将响应 传送回使用者,响应指示成功(或错误),并且包含服务的结果。通过这种方式,使用者可以调用提供者的端点,传入请求,并得到响应。

目前,定义实现 Web 服务的方式的是 WS-I Basic Profile 1.1,该概要包括 SOAP 1.1 over HTTP 1.1,后者由 Web 服务描述语言 (WSDL) 1.1 进行定义。(请参阅参考资料以获得指向规范本身的链接。)有了 SOAP over HTTP,使用者可以通过 HTTP 请求中的一个绑定 HTTP 消息传输的 SOAP 请求调用服务。使用者同步阻塞 HTTP 套接字,等待包含 SOAP 响应的 HTTP 响应。端点的 API 是由使用者和提供者之间的约定描述的。

既然您了解了相关术语,就让我们来看一看使用者用于调用服务的通信选择:同步或异步。

同步与异步调用

使用者可以同步或异步实现服务调用。从使用者的观点来看,这两种方式的不同之处在于:

  • 同步——使用者通过单个线程调用服务;该线程发送请求,在服务运行时阻塞,并且等待响应。
  • 异步——使用者通过两个线程调用服务;一个线程发送请求,而另一个单独的线程接收响应。

术语同步异步 经常与顺序并发 混淆了。后面的这两个术语与执行单独的任务必须遵循的顺序有关,而同步异步 与线程执行单个任务(如调用单个服务)的方式有关。理解同步和异步调用之间的不同的一种很好的方法是考虑崩溃恢复的后果:

  • 同步——如果使用者在服务运行的过程中阻塞时崩溃了,当它重新启动时,将无法重新连接到正在进行的调用,所以响应丢失了。使用者必须重复调用过程,并且期望这次不会崩溃。
  • 异步——如果使用者在发送了请求之后等待响应时崩溃了,当它重新启动时,可以继续等待响应,所以响应不会丢失。

崩溃恢复不是同步和异步调用之间的唯一不同,但是如果您尝试确定某个调用采用哪一种方式,请考虑每一种调用如何处理崩溃恢复,这通常可以给您一个很好的答案。

既然您了解了使用者对服务调用通信方式的选择,就可以看一看使用者对用于连接到提供者的连接方式的选择。使用者可以从下列通信方式中选择一种:

  1. 同步直接调用
  2. 同步代理调用
  3. 异步代理调用

我将分别解释每一种方式。

同步直接调用

调用 Web 服务的 SOAP over HTTP 方式就是直接的:非常类似于执行函数调用,使用者知道端点的地址,并直接调用它。为了使调用成功,Web 服务必须在使用者调用端点时可用,而且必须在使用者超时之前进行响应。如果将 Web 服务部署到新的位置(例如不同的 Internet 域),则必须让使用者知道端点的新 URI。要部署具有相同服务类型的多个提供者,必须将每个提供者的端点部署到不同的 URI。要在不同的服务提供者之间进行选择,使用者必须知道其中的每个 URI。

例如,考虑一个简单的用于获取股票报价的 Web 服务:使用者传入股票代号,然后取回股票的当前价格。此服务可能由多个不同的代理公司提供,每个公司都有一个不同的 Internet URL。获取 Web 服务的 URL 是一个先有鸡还是先有蛋的问题。如果使用者知道端点的位置,它就可以询问服务其地址是什么,但是使用者需要知道地址才能询问地址。

为了解决这个问题,统一描述、发现和集成(Universal Description Discovery and Integration,UDDI)描述了一种 Web 服务,它是一个类似于电话簿的目录,用于查找其他的 Web 服务。其思想就是,将 UDDI 部署到一个使用者已经知道的知名地址,然后使用者就可以使用 UDDI 来查找其他的 Web 服务。

对于股票报价服务,使用者知道 UDDI 服务的地址,而 UDDI 服务又知道股票报价服务的地址,如图 1 所示。

图 1:直接调用 Web 服务

图 2 展示了使用者如何使用 UDDI 服务来查找股票报价提供者的端点,并且调用其中的一个端点。该流程的工作方式如下:

  1. 使用者向 UDDI 询问服务提供者列表。
  2. 使用者从 UDDI 返回的列表中选择一个提供者的端点。
  3. 使用者调用该端点。
图 2:同步直接服务调用

请注意,选择提供者的算法完全由使用者决定;在本例中,使用者只选择列表中的第一个。实际实现可能要复杂一些。

还需要注意的是,因为服务端点可能改变,所以当使用者每次需要调用服务时,都应该重新查询 UDDI,查看提供者的详细信息是否有改变。必须为每次服务调用查询 UDDI 大大增加了调用服务的开销,特别是在提供者的详细信息通常不改变的情况下。

同步代理调用

直接调用方法的不足之处在于,使用者必须知道提供者的端点的 URI 才能调用服务。它使用 UDDI 作为查找 URI 的目录。如果提供者更改其端点的 URI,它必须向 UDDI 服务器注册,这样 UDDI 就有新的 URI,然后使用者必须重新查询 UDDI 以获得新的 URI。事实上,这意味着每次使用者需要调用服务时,它都必须查询 UDDI 以找到端点 URI,并从中进行选择。这导致使用者把许多时间浪费在重复查找 UDDI 和选择提供者这样的工作上。这种方法还使得使用者必须以某种方式从看起来不可区分的列表中选择提供者。

简化这个问题的一个方法是引入 Broker,作为调用 Web 服务的中介。使用者不再直接调用服务提供者,而是调用 Broker 中的服务代理,而服务代理又调用服务提供者。使用者需要知道代理端点的 URI,以便使用 UDDI 查找地址,但是在本例中,UDDI 只返回单个 URI,因而使用者不必选择。使用者甚至没有意识到端点在代理中;而只是知道它可以使用此 URI 来调用 Web 服务。Broker 协调使用者与服务提供者,如图 3 所示。

图 3:同步企业服务总线

代理的 URI 应该是稳定的:在使用者使用 UDDI 获取代理的 URI 之后,它第一次调用服务,在以后的调用中,使用者可以重用该 URI(至少在代理停止工作之前)。同时,代理跟踪提供者及其 URI(可能在调用之间发生改变)、它们是否可用(上一次调用失败了吗?)、它们的负载(上一次调用花了多长时间),等等。然后,代理负责为每次调用选择最好的提供者,从而免去了使用者这方面的责任。使用者每次都在同一地址调用同一代理,代理负责协调各个提供者。

图 4 展示了使用者如何使用 Broker 调用服务,工作方式如下:

  1. 使用者向 UDDI 请求服务提供者列表。UDDI 返回的 URI 实际上是服务代理的 URI。UDDI 只返回一个 URI 而不是多个 URI,因为 Broker 只将一个代理用于特定的服务。
  2. 使用者使用代理的 URI 调用服务。
  3. 服务代理从其可用提供者列表中选择服务提供者。
  4. 服务代理调用所选提供者的端点。
图 4:同步代理服务调用

请注意,选择提供者的工作已经从使用者转走了,现在封装在 Broker 的代理中。这简化了使用者的工作。最后,代理使用的选择流程可能不同于使用者使用的选择流程。但是,因为 UDDI 服务器和代理都封装在 Broker 中,所以可以更容易地提高某些方面的效率,例如在代理中缓存信息、在缓存的信息变得过时让 UDDI 服务器通知代理。

还需要注意的是,因为代理的地址是稳定的,所以使用者不必重复地查询 UDDI,每个服务调用只需查询一次。更确切地说,使用者只需查询 UDDI 一次,然后就可以安全地缓存代理的地址,并且重复地使用它来调用服务。这大大地降低了调用服务的开销。

异步代理调用

同步方法的不足之处在于,在执行服务时使用者必须阻塞——在服务运行时线程必须阻塞。如果服务花很长时间执行,使用者可能会在接收到响应之前放弃。当使用者发出请求时,如果没有一个服务提供者正在运行或者它们都过载,则使用者将无法等待。如上所述,如果使用者在阻塞时崩溃,则即使它重新启动,响应也会丢失,因而必须重新进行调用。

解决这个问题的常见方法是使用者异步调用服务。通过这种方法,使用者可以使用一个线程来发送请求,而使用另一个线程来接收响应。这样,使用者就不必阻塞以等待响应,而且可以同时执行其他工作。因此,使用者对花多长时间执行服务不太敏感。

支持使用者异步调用 Web 服务的 Broker 是通过消息传递系统实现的,消息传递系统使用消息队列来发送请求和接收响应。与同步消息代理一样,这一对消息队列担当使用者用来调用服务的单个地址,而不管多少提供者可能正在侦听,如图 5 所示。

图 5:异步企业服务总线

这种方法使用请求-响应模式来调用 Web 服务。与 WS-I BP 1.1 中指定的 HTTP 不同,消息队列现在执行传输。SOAP 请求和响应与 WS-I BP 相同,但是它们现在包含在消息系统的消息中。

图 6 展示了使用者如何使用 Broker 异步调用服务,具体步骤如下:

  1. 使用者以请求队列中的消息的形式发送 SOAP 请求。现在,使用者的工作已经完成了,可以使用该线程来执行其他工作。
  2. 每个提供者都可以看到请求队列中的使用者,这使得它们要竞争使用者。消息传递系统确定哪一个提供者能够接收消息,并确保只有一个提供者接收消息。具体工作方式取决于消息传递系统的实现。
  3. 获胜的提供者从请求队列接收消息。
  4. 该提供者执行服务。
  5. 该提供者以应答队列中的消息的形式发送 SOAP 响应。现在,提供者的工作已经完成了,可以使用其线程执行其他的工作(例如等待另一个请求)。
  6. 使用者的侦听器线程接收包含 SOAP 响应的消息。
图 6:异步代理服务调用

请注意,选择提供者的工作现在封装在消息传递系统中,从而简化了使用者的工作。还需要注意的是,如果使用者在发出请求之后崩溃,则即使响应在这个期间返回,消息传递系统也会将响应保存在应答队列中,直到使用者再次启动为止。

同时需要注意,使用者不使用 UDDI 查找请求队列和应答队列。目前,没有用于返回队列地址对的标准服务,所以使用者必须确切地知道这些地址。使用者要么与这些地址硬编码在一起,要么从外部配置文件中读取它们。将来,需要扩展 UDDI 或者指定类似的服务供使用者查询,以便发现用于调用特定服务的队列对。

现在,您了解了服务调用的连接方式选择。接下来,让我们看一看也可能有用的其他集成功能,然后向您展示如何开发一个 ESB 来提供这些功能。

其他集成功能

使用 ESB,您还可以超出服务调用,并且使用其他技术集成应用程序和 SOA 的各个部分。服务调用几乎始终是双向操作,这意味着请求是从使用者发送到提供者的,而响应是按照相反的方向返回的。其他的集成技术是以单向操作的方式进行工作的,其中,发送方将信息发送到接收方而不等待响应;接收方只是使用信息而不进行响应。

数据传输

有时,应用程序只需将数据传输到另一个应用程序,而不必调用接收方的过程,而且肯定不等待结果。这是一个典型的集成问题:一个应用程序有数据,而另一个应用程序需要数据。发送方不需要告诉接收方如何处理数据;它只需使数据可用即可。

可以通过服务调用来传输数据,这等同于调用 setter 方法,但是使数据传输到 RPC 范型中。数据传输实际上更类似于文件传输:数据从发送方导出并导入接收方,不需要发送方公开地指导接收方如何处理数据。这更类似于文档样式的 SOAP 消息而不是 RPC 样式的消息。

用 ESB 进行数据传输可以查找接收方,并可靠地传输数据。发送方不必知道如何查找接收方,它只需知道如何查找 ESB 并信任 ESB 将找到接收方即可。ESB 还负责可靠地传输数据。发送方只需将数据传送到 ESB 并知道数据将传递即可。

有关数据传输技术的详细信息,请参见文档消息 (Document Message) 模式。(有关这方面的详细信息,请参阅参考资料中列出的 Enterprise Integration Patterns 一书。)

事件通知

有时,需要将在一个应用程序中发生的更改通知给其他应用程序。例如,如果使用者在一个应用程序中编辑其地址,则应该通知其他的应用程序以及它们自己的数据库,以便它们可以更新其记录。

此外,一个应用程序可以对另一个应用程序调用服务来通知其更改情况,但是这种方法有三个问题。头两个问题与数据传输相同。首先,服务调用对接收方应该如何处理信息知道得太具体了,其次,它往往是双向的,这使得发送方必须等待(甚至同步等待)它并非真正需要的应答。

通过调用服务进行事件通知的第三个也是最重要的是一个问题是,服务调用在本质上是一对一的,即使用者对提供者,而事件通知在本质上是一对多的,需要广播到所有相关提供者。使用服务调用,发送方必须跟踪所有相关的接收者,并且分别对其中的每一个进行服务调用。这种通知广播最好留给发送方和接收方之间的 Broker。

用 ESB 进行消息传递可以跟踪相关接收方并确保通知传递到每一个接收方。通过这种方法,发送方只需发出一次通知,即可确保通知传递到所有的相关接收方,而不管这些接收方是谁。因为此操作是单向的,所以在传递通知时发送方可以同时做其他工作,而且可以并发传递通知。

有关数据传输技术的详细信息,请参见事件消息 (Event Message) 模式。(同时参阅参考资料中列出的 Enterprise Integration Patterns 一书。)

开发企业服务总线

现在,您知道了直接调用提供者中的 Web 服务和使用 Broker 进行调用之间的区别。您也了解了 Broker 如何支持使用者同步或异步地调用服务。

这样的 Broker 常常称为 ESB。那么,与已为大家接受的设计和模式相比,ESB 有什么特点呢?

自描述和可发现

Web 服务与以前的集成方法的不同之处在于,使用者可以动态绑定到服务的提供者。之所以能够这样做,是因为具有以下两种主要功能:

  • 自描述——Web 服务可以以机器可读的方式描述自身。同一服务的两个或更多提供者即使具有完全不同的实现也可以立即识别出来,因为它们的声明性接口符合相同的描述。
  • 可发现——Web 服务提供者可以组织到机器可执行的目录中。使用者可以搜索这样的目录来查找所需服务的提供者。

这些自描述和自发现功能完全不同于以前的集成方法。使用其他方法,将在编译时执行接口,与此同时使用者将被绑定到提供者。消息格式不是以声明的方式表示的,而是暗含在双方的约定中,并且在接收方成功地解析发送方创建的结构之前是不可执行的。

自描述服务通过声明可以执行的接口简化了集成。动态发现服务使得不需要在特定的地址将使用者绑定到特定的提供者,但是运行时发现本身也带来了一些问题。使用者应该一次性地发现服务的提供者还是重复地发现服务的提供者?

在编译时一次性将使用者绑定到提供者与在运行时潜在地针对每次调用发现新的提供者之间作出取舍非常困难。ESB 提供了第三种选择,承诺在支持使用者动态地一次性绑定到服务代理的同时,仍然能够通过代理使用多个提供者和选择新的提供者。

因此,ESB 不仅使服务可用以便使用者能够调用它们,而且为使用者提供了以编程方式查找服务的功能。

服务网关

同步 ESB 的基础称为服务网关,它充当服务使用者和提供者之间的中介,以促进同步代理调用。通过服务网关,可以访问所有知名的服务和其中每个服务的代理。采用这种方式,网关可以为希望调用该网关代理的任何提供者的任何服务的使用者提供一站式服务。

如果网关协调的服务是 Web 服务,则这些服务是自描述的。每个服务都使用 WSDL 声明其接口,WSDL 由以下四个部分组成:

  1. 端口类型——Web 服务执行的操作集。端口类型可能是带有端口/操作(如 getQuote)的 stockBrokerServices
  2. 消息——请求和响应的格式,例如 getQuoteRequest(包含股票代号)和 getQuoteResponse(包含价格)。
  3. 类型——Web 服务使用的数据类型,例如代号和价格(或者只是 xs:stringxs:decimal)。
  4. 绑定——调用操作的地址,例如 http://stockbroker.example.com/getQuote。

这样的网关的服务——或者更具体地说,其服务代理——也是可发现的。如前所述,网关以 UDDI 服务的形式提供这种功能。要发现调用服务的地址,使用者可以查询网关的 UDDI 服务,以找到所需 WSDL 操作的提供者,并取回该操作的网关代理的绑定。

消息总线

异步企业服务总线的基础是已为大家接受的模式,称为消息总线 (Message Bus),如参考资料中列出的 Enterprise Integration Patterns 一书所述。消息总线是消息通道(也称为队列或主题)的集合,通常配置为请求-应答通道对。每一对都表示使用者可以通过总线调用的服务。调用方将请求消息放在服务的请求队列中,然后(异步)侦听应答队列中的结果。(调用方知道哪个结果对于特定的请求是正确的,因为它有正确的关联标识符。)

调用服务的使用者实际上并不知道谁在提供服务。服务提供者也连接到消息总线,并侦听请求消息。如果有多个服务提供者,则它们实际上将相互竞争,以便成为发出特定请求的使用者的服务提供者。实现消息总线的消息传递系统充当消息调度程序,并且将请求消息分发给服务提供者,在理想情况下,将根据负载均衡、网络延迟等以某种方式优化这种分发。在服务提供者接收请求之后,它执行服务,然后将结果放在达成一致意见的应答通道中的消息内。这样,提供者和使用者从不直接知道彼此的地址;它们只知道消息总线和如何查找适当的通道的地址,而且通过共享相同的通道,它们可以进行通信。

消息总线是 ESB 的基础,并且不是什么新鲜事物。应用程序集成人员已经使用消息队列产品(如 WebSphere® MQ 和 TIBCO Enterprise Message Service)做这项工作十多年了。其实,人们常说,如果您正在使用企业消息传递产品,您就有了 ESB。IBM 客户已经使用 WebSphere Business Integration Message Broker 和 WebSphere MQ 这样做了很长时间。

那么,ESB 就是消息总线吗?不,消息总线肯定是异步 ESB 的基础,但完整的 ESB 还包括其他的功能。ESB 具有消息总线一直缺少的功能:即上述描述和发现功能。

更好的消息总线

如此说来,如果消息总线不是完整的 ESB,那么 ESB 还可以做什么呢?

传统消息总线方法的不足之处在于,它不是自描述的。从使用者的角度来看,有许多服务,也有许多用于调用服务的通道。但是哪一个通道是用来调用使用者所需的服务的合适通道呢?使用者不能将请求随便放到一个请求通道中,它必须知道用于调用其所需的特定服务的合适通道。否则,它可能事与愿违,明明需要的是一本书,但最后买到的却是一张飞机票。另外,即使使用者(以某种方式)知道了要使用哪一个通道(以及要侦听哪一个通道以获得应答),它也需要知道请求中的数据应该采用什么格式(以及应答需要采用什么数据格式)。

如上所述,WSDL 为同步 Web 服务解决了这个问题,并且暂时也是描述异步服务的标准选择。与请求通道相关的 WSDL 描述通道提供什么服务,以及使用者必须提供的请求消息的格式。WSDL 还可能指定调用方应该侦听以获得应答的应答通道,以及应答消息必须具有的格式。采用这种方式,调用方应用程序可以以编程方式查看用于调用服务的通道对,并且知道它们以所要求的请求和应答消息格式提供了所需的服务。

自描述服务通道带来了另一个问题,即通过 UDDI 发现哪些同步 Web 服务。如上所述,使用者向 UDDI 服务器请求 Web 服务提供者的地址,而该服务器以提供者的 URL 应答。然后,使用者使用该 URL 来调用该服务。

ESB 需要类似的目录服务,一个带有类似于 UDDI 的 API 的服务,使用者可以调用这样的服务,来请求实现所需的 WSDL 操作的服务的地址。ESB 以合适的请求-应答通道对应答。所以 ESB 使用者(如 UDDI 使用者)只需知道以下内容即可:

  1. 描述需要调用的服务的 WSDL
  2. ESB 的目录服务的地址(它可能派生于 ESB 的根地址)

对于查找服务的请求与应答通道和开始调用服务,这足够了。此外,这个目录服务还是 ESB 提供的另一个服务,查找其他服务的主服务。

同步还是异步?

服务使用者需要在通信方式之间做出选择:同步还是异步?为了解决这个难题,许多 ESB 将同时支持同步和异步服务,并且事实上可以为同一服务提供两种调用模型。在这种情况下,当使用者请求服务地址时,它可以获得两个匹配地址:一个用于同步,一个用于异步。然后,使用者可以选择它最喜欢的调用模型。不管采用哪一种方式,执行的服务都是相同的,不过具体的服务提供者实例可能有所不同。

所以 ESB 比传统的消息总线要好,因为它还使得服务可以自描述,并且提供了用于查找其他服务的目录服务。这正是构建 ESB 的产品供应商努力提供的。

结束语

可以看出,服务可以通过以下三种方式之一进行调用:

  1. 直接同步
  2. 通过 Broker 同步
  3. 通过 Broker 异步

企业服务总线是支持同步和异步调用的 Broker。它还支持应用程序之间的数据传输和事件通知。它帮助使用者查找提供者和处理提供者之间的通信的细节。

同步 ESB 是充当各种服务的中间协调者的服务网关。异步 ESB 是其服务还支持自描述和可发现 Web 服务功能的消息总线。目前存在用于实现同步 ESB 和消息总线(简化的异步 ESB)的标准和模式。异步 ESB 还需要其他标准,以便充分发挥他们的潜力。