如今,企业服务总线是一个有用的解决方案,这一点毋庸置疑。它和一组工具相结合一起解决了应用与服务集成领域的实际问题。但是,它们给不熟悉它们的使用者所带来的轻微不便却和工具箱一样。那些使用者知道问题的解决办法肯定在箱子内,但却不知道解决问题的工具是哪个!
本文的目标是帮助ESB使用者在面对复杂多样的ESB概念——路由和编配——时,根据他们的需要选择合适的工具。我们并不打算宣讲抽象的理论,相反我们将把我们的努力和论证植根于一些简单的、现实世界的例子(它们使用了一个JBI兼容的ESB——OW2 PEtALS[1])中,并尝试填补低级别路由和全局的业务服务编配之间的空白。换句话说:我们将尽力揭示路由和编配的不同层级是如何形成的。
ESB涉及多个应用领域,包括实现信息系统范畴的面向服务架构(SOA)。但它们的基本目的都是为了简化应用和服务的集成——简而言之就是让一个应用或服务去调用另一个应用或服务。这种非常简单和平凡的事业有各种额外的复杂级别:
它们三个:路由、协议和转换都有不少近亲,虽然如此,但是它们三个仍被认为是主要的ESB概念。在本文中,我们将关注第一个,以及它如何关联到它的一个近亲:编配。在此先对它们进行一个简短介绍,我们认为路由从根本上说是低级别的,它在ESB核心之中或附近,且依赖技术配置(如服务部署描述符)来提供与消息必须被发到的目的地相关的技术决策。编配可被看成是将多个服务调用组合为高级、更有用的组合服务的过程,但它也通常被打上了“业务级”的印记,此时它代表了跨应用和信息系统实现结合了特定业务服务的业务级流程的缩写。
那么,在ESB中是如何处理编配需求的呢?看上去使用一个配备有中间件解决方案的编配引擎更符合逻辑。可是,这是个没法简单回答的复杂问题。让我们考虑如下例子。
显示一个项目(item)列表
“ItemManager”应用用于通过创建、更新、删除等操作对项目进行管理。一个“ItemManagementListener”服务连接到了这个应用上,它负责在项目被更新时发布通知。
另一个应用:“HammerMonitor”是一个监测工具,用于显示锤子相关项目的更新信息。这个应用暴露了一个“HammerMonitor”服务,它包含了一个接收这些通知的“显示(display)”操作。
这两个服务都暴露给了一个ESB。我们的目的是想让HammerMonitor显示ItemManagement应用知道的锤子相关的项目信息。
为了让ItemManagementService连上HammerMonitorService,我们需要配置若干ESB连接器(connectors)(所谓的“绑定组件”)。一个连接器被链接到ItemManager应用,另一个被链接到HammerMonitor应用。
此外,跟HammerMonitor链接的连接器被配置成在ESB内部暴露一个名字为“hammerMonitorService”的端点(endpoint)。于是,实现我们目标的一种简单方法就是对ItemManager所链接的连接器加以配置,使它在每次收到来自 ItemManager的消息时就在ESB内部调用端点“hammerMonitorService”。
可是,正如现实世界经常发生的那样,我们假设这两个服务拥有不同数据的格式。这对SOA并不构成障碍,因为SOA定义了一个松耦合的架构(即,服务消费者并不必须符合服务提供者定义)。
ItemManagement应用向ItemManagementListenerService提供了以下消息:
<items>
<item type="Hammer" name="hammer1"/>
</item>
而ItemMonitorService的“显示(display)”操作使用以下格式:
<hammers>
<hammer hammerName="hammer1"/>
</hammers>
这时,只是简单地进行调用并不能把两个服务链接起来。ItemManagement应用提供的数据必须首先经过转换。这实际上是一个非常简单、局部的编配需求,跟业务级没有任何关系。
解决这个问题的第一种方法是,使用一种常见、知名的编配解决方案,如成熟、外部部署、支持BPEL的编配引擎[2]。这种方法可以行得通,但是用在这个例子中就好比杀鸡用牛刀:要么所有被转换消息必须经过一个集中、远程的编配引擎,这种方式类似过时的“集线器(hub)”集成架构;要么就必须在每个节点都部署一个编配引擎——对于这个简单问题来说,这种解决方案显然过于复杂了。
那么,似乎单一、全局、业务级的方法不足以解决编配需求:当总线提供的通用路由能力不够且主要问题还不是通过操纵受SOA管理的业务服务来实现业务规则或流程,而仅仅是结合技术性、“幕后”服务,这样它们就能“把事情搞定”的时候,路由层和业务层之间必须做的“脏”活怎么办?
解决路由和编配需求的最基本方法是增强ESB的内置特性。
在我们上面的例子中,一种直接规避发送消息应用和接收消息应用之间数据一致性问题的方法就是:在连接器(即,ESB的绑定组件)内部增加一些逻辑。
例如,PEtALS ESB提供的绑定组件就能使用“拦截器”进行扩展。拦截器是一段Java代码,它在一条消息被发送到总线之前在“发送者”绑定组件内部执行,或在一条消息被交付后在“接收者”组件内部执行。
在我们的例子中,这个代码可以调用一个XSL转换将ItemManagement的消息格式改编成HammerMonitor的格式。
不过,这种方法有很大的限制且不够彻底。如果XSL转换发生在“接收者”连接器(HammerMonitor链接的那个连接器)中,它将假定所有接收的消息都具有ItemMangement的XML结构。假如有条消息来自另一个应用,它具有不同的结构,这种情况下XSL转换就会失败。
拦截器可以检查传入消息结构,根据检查结果选择不同的XSL转换,但是这将和发送者耦合非常紧密。这种方式不符合SOA的松耦合概念。而且,除了转换之外的任何其他需求也意味着要在ESB引擎内开发另一组相关的特性,这不是ESB使用者所期望的,也不应该是ESB所期望的。
ESB通过供应集成组件提供了集成设施。这些组件可以完成一组消费者和服务提供者之间细小、有用、灵活的操作。它们一般都实现了几个企业集成模式(它的知名,Gregor Hohpe功不可没[3]),是ESB使用者的瑞士军刀。
这些EIP组件不依赖服务描述(WSDL等),它们仅仅执行一些细小的任务。最知名的是:
具备所有EIP组件操作的知识将使得开发者可以把业务应用(消费者和服务提供者)和一些“集成模式单元”结合起来。最终结果是一个组合集成。每个集成单元是一个服务。
当然,要想设计这种组合集成,一种专用的图像IDE非常重要。因为,除了简化使用,它还带来了所有单元配置的集中视图。例如,以下几个例子就是使用PEtALS ESB集成工具设计的。
管线(pipeline)模式被用来将一条传入消息“输送”给几个服务。这条消息被发送给第一个服务,第一个服务的响应被发送给第二个服务,第二个服务的响应发送给第三个,依此类推。
我们刚刚描述的ItemManagement用例可以设计成这种结构:一个转换组件和一个“管道”单元。
按以下方式,相同的行为也可被用来管理服务版本的演变。消费者总是向“管道”单元发送相同的消息结构,“管道”单元是真实服务的代理。一旦服务签名改变,“管道”单元首先将消费者消息发给XSL转换(将消费者的消息改编成新服务格式),然后再将其发送给服务的新版本。消费者不需进行任何改变。
我们已经了解了如何将几个服务组装成一个服务。但是动态流程方面的问题还没有得到解决。这里再次遇到了路由挑战:如何调用多个服务中的一个?
如何在多个服务之中将调用切换到某个服务?嗯,路由器单元可能可以执行一些测试来将请求切换到某个版本或其他版本的服务上。
例如,ItemManagementListener可以向一个“基于内容路由”的组件发送关于锤子和锯子的通知。这个组件测试消息中的项目名,然后将它发送给正确的监测服务(HammerMonitorService或SawMonitorService)。由于每个服务定义了不同的格式,在发送消息给正确的服务之前必须完成两种不同的转换。这样,我们就将“路由”单元和“管道”单元与“转换”单元组装在了一起。
另一种集成需求可能是将一个请求发送给不同的服务(多点通信)。例如,在一个项目订单从前端应用发送给订单系统的时候,一封要求确认的电子邮件也可能同时被发送给客户。例如,消息被发送给了订单服务和SMTP服务。
我们可以这样设想,ItemManangementListener服务(它负责发送来自ItemManagement应用的通知)必须将通知发布给HammerMonitor、SawMonitor和一个全局监测工具(它接收所有通知)。
可以给上面的组合集成增加一个“分发器”集成单元,它负责给“路由”单元和全局监测服务发送消息。
企业集成模式是帮助构架路由和编配解决方案的重要概念,而且EIP组件是能够切实设计这些问题的解决方案的绝妙工具。然而,在复杂的集成环境下,组合结构的方法极易导致过于分散和过度设计的配置。而且和许多模式一样,EI模式个数有限,而现实世界充满了需要更灵活解决方案的意外情况。
答案是使用轻量级、编配相关的DSL(领域特定语言),它就是PEtALS中提供的“轻量级编配器”或“企业集成编配”组件。
什么时候适合使用这种组件?这依赖于很多方面,包括开发实践,但是这里有些提示:
为了显出EIOrchestration组件的优点,让我们关注一下我们系统的扩展性。
我们已经知道了如何给一个最初只能处理锤子的系统增加一个监测锯子相关项目的特性。我们可以按同样的方式增加其他工具相关的功能。但是,这要求每当我们想要增加另一个工具类型时都对系统进行重新配置。那么,如果我们想让我们的总线使用者能够增加他们自己的工具类型和相关的监测功能呢?
例子:我们的客户想能动态增加针对螺丝起子类工具的ScrewdriverMonitorService,以及针对转孔机的DrillerMonitorService等等。
我们可以告诉他们:在每条消息中都给出它必须被发送给的工具相关的监测服务名,然后给系统增加动态路由功能。
例子:我们对ItemManagement进行了增强,它向ItemManagementListenerService提供以下消息体:
<items>
<item type="Screwdriver" name="screwdriver1"
customMonitorService="ScrewdriverMonitorService"/>
</item>
其中增加了customMonitorService数据字段,它可能是客户通过ItemManagement应用提供的。
在ESB中,可以通过根据“customMonitorService”属性动态选择消息的接收者路由这种消息。例如,在PEtALS内,可以利用EI编配组件,使用它的“get-calls-by-xpath”特性办到:
<eip:get-calls-by-xpath base="/items/item" service="@customMonitorService"
operation="'display'"/>
在我们的例子中,它会使用前面的消息调用ScrewdriverMonitorService消息。
在一开始,我们就已经说过PEtALS EIOrchestration组件可以很好地处理流程复杂性。那么,这儿有个例子说明了这一点。它在单个配置中收集了我们目前在文中已经看到的全部内容:管道(“eip:chain”元素)和转换,简单的基于内容的路由(“eip:choose”元素)和最后的动态路由(“eip:get- calls-by-xpath”元素),同时兼具良好的可读性
<eip:eip>
<eip:chain>
<eip:choose>
<eip:when test="/items/item[0]/@type = 'Hammer'">
<eip:call service="ItemToHammerService" operation="transform"/>
<eip:call service="HammerMonitorService" operation="display"/>
</eip:when>
<eip:when test="/items/item[0]/@type = 'Saw'">
<eip:call service="ItemToSawService" operation="transform"/>
<eip:call service="SawMonitorService" operation="display"/>
</eip:when>
<eip:otherwise>
<eip:get-calls-by-xpath base="/items/item"
service="@customMonitorService" operation="'display'"/>
</eip:otherwise>
</eip:choose>
</eip:chain>
</eip:eip>
另一种设计集成的方法是自顶向下的方式,其中会定义企业业务流程。在这种方法中,业务流程驱动了业务服务的定义。因而,在现有应用提供的服务和业务流程期望定义的编配之间需要建立桥梁。这种桥梁表现为企业信息系统内部所有受管理的业务级别服务集合,即它的SOA(面向服务架构),对于低级别、总线上的技术服务和实际业务流程来说,它都扮演了一个保护层的角色。
在SOA的世界中,执行流程的标准方式是使用一个BPEL引擎[2]。它可以调用几个服务并能在流程和XML文档中完成一些业务逻辑,同时还能够处理数据映射问题。在这种方式中,业务服务定义是编配的关键:哪怕缺少一个服务的定义(一般是WSDL)都会导致BPEL编配无法完成,因而你应该确保更清晰的(然而也是更贵的)服务组合。
假如要在ESB中使用BPEL,可以参考Adrien LOUIS书写的文章《使用现有服务构建SOA应用》,它提供了关于编配装置的一般性介绍[4]。
现在,在我们的工具监测例子中,如果我们想要实现在管理员批准后方能在监测应用中实际显示信息,怎么办?这可能需要专门设置一个操作员来负责进行人工干预。这是业务流程管理的另一面:工作流,它是允许手工、人类操作干预的业务流程,要么因为手工业务任务,要么因为人工监督,通过业务门户或更技术性的管理界面提供的图形用户界面完成。
关键点在于,与象BPEL编配这样的基于流程的方法不同,工作流遵循的是相反的范式,基于状态的方法。这使得它们更适合长生命周期的流程,没有建构在编配服务之上的限制。因而,“正统的”编配是对工作流服务器的有益补充,通过部署两套面向业务流程的服务器的方法——部分地通过一些有趣的新项目得到了解决,如jBoss & Bull的“过程虚拟机”和Eclipse Java工作流工具项目[5]。
在这篇文章中,我们已经了解了几种将业务服务彼此互联的方法,从使用如自定义路由这样的低级别方式,到使用如工作流和编配这样的面向业务的高级别方式。更为重要的是,我们已经展示了ESB集成器对组合本地、技术服务的中间级需求有多么普遍,以及一组“胶水”、“瑞士军刀” 式的特性是如何令它们简单地“把事情搞定”的。
总而言之:
Adrien Louis是EBM WebSourcing的PEtALS ESB首席架构师和SOA顾问,他拥有8年从事Java EE技术和信息系统集成经验。
Marc Dutoo是Open Wide的开源解决方案架构师,该公司是法国领先的开源门户集成商。他的兴趣包括SOA、BPM和内容管理。Marc还是Eclipse Java工作流工具项目的联合组长。
查看英文原文:Choosing between Routing and Orchestration in an ESB
志愿参与InfoQ中文站内容建设,请邮件至[email protected]。也欢迎大家到InfoQ中文站用户讨论组参与我们的线上讨论。