内容


IBM WebSphere Enterprise Service Bus V6.1 中的聚合功能,第 3 部分

最佳实践和聚合模式

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: IBM WebSphere Enterprise Service Bus V6.1 中的聚合功能,第 3 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:IBM WebSphere Enterprise Service Bus V6.1 中的聚合功能,第 3 部分

敬请期待该系列的后续内容。

引言

为帮助讨论,已将本文介绍的四种模式中的每一种模式作为项目交换文件提供的实现应用到实际的业务场景中(请参阅下载部分访问这些文件)。请注意,您应将这些实现视为业务场景所基于的蓝图,因为它们对您要考虑的关键点提供指导。

一般概念

WebSphere Enterprise Service Bus V6.1 支持构造聚合场景的灵活机制,并允许中介开发人员使用许多不同的方法合并元素来生成解决方案。不过,在设计使用聚合的流时,您必须注意:

  • 按正确的模式使用元素。
  • 创建和使用共享的上下文。
  • 正确地使用转换支持。

其中每一项都在本文描述的四种基本模式中进行了重点介绍,这四种基本模式为:

  1. 来自多个来源的数据聚合:如果需要将来自多个服务调用和数据库查询等的结果合并成单个消息,则此模式非常有用。
  2. 使用消息充实的批处理:如果要处理的消息包含重复元素,并且需要单独操作每个元素才能对其增强,则此模式非常有用。
  3. 需要嵌入聚合的批处理:如果场景是上面第二种模式中描述的基本批处理模式,但是增强需要来自多个来源的信息,则此模式非常有用。从这个方面而言,此模式是第一种模式和第二种模式的合并。
  4. 嵌套的聚合:如果场景是第二种模式中描述的基本批处理模式,但是重复元素本身包含重复元素,则此模式非常有用。

场景 1:来自多个来源的数据聚合

此场景基于保险报价比较网站,该网站涉及允许用户指定报价详细信息的 Web 接口。业务处理引擎可控制进程,并将单个请求提交给聚合中介,该聚合中介将此请求再提交给多个保险公司。然后在显示给用户之前,将响应聚合成业务进程操作的单个响应。此场景是在本系列的第一篇文章中分步构建的;本部分将讨论此场景的关键设计注意事项。(下载场景 1 文件。)

设计聚合

流的输入接口是 InsuranceQuote,它使用相关的数据指定 Quote 业务对象。规划聚合体系结构的第一个步骤是确定调用后续服务所需的所有数据是否在入站消息中提供,或者是否需要消息充实。在此场景中,Quote 对象包含用于两个服务的所有信息。如果不是这样,则您可以使用此模式与使用消息充实的批处理 模式的组合模式。事实上,对于此特定场景,保险的后端服务接口采用与中介接口一样的业务对象。这意味着服务调用原语之前的转换非常简单;不过,在更实际的情形中,通常需要更复杂的转换。

中介流包括扇出原语(一次性操作模式),它接收 Quote 消息作为输入,并将逻辑分发到每个保险后端服务(InsuranceCompanyA 和 InsuranceCompanyB)。在转换每个服务调用原语之前,XSLT 将消息正文转换为正确的格式,并在转换服务调用原语之后,Message Element Setter 立即将响应存储在共享上下文中,以便在稍后构建聚合的响应时进行检索。扇入原语然后收集这两个分支(在 count 模式下操作)。当第二个消息到达扇入原语时,其输出末端由此消息触发,因此,最终的 XSLT 将共享上下文中的值转换成聚合的响应消息。此过程如图 1 中所示。

图 1. 保险报价流程
保险报价流程
保险报价流程

在本例中,共享的上下文非常简单,仅包含响应消息(来自两个后端保险服务)的各个组成部分,需要将其存储才能生成聚合响应(请参见图 2)。

图 2. 用于保险报价的共享上下文
用于保险报价的共享上下文

最终的 XSLT 然后构建直接发回的响应消息;此场景不包括响应流,因为所有的出站请求都是在中介流的请求流中完成的。选择此设计是因为不存在任何明显的单个调出;因此可以将逻辑合并到一个流中。

这是一种非常简单的聚合使用模式,但是将其合并到更复杂的场景中时,其简单性会让人感到与其强大的功能不相符,这在本文档后面将有叙述。

场景 2:使用消息充实的批处理

此场景基于存储订单系统,其中推销员可以为大量订单提交请求,并分派它们。引入中介流的初始消息通过客户 ID 标识每个订单的客户。这样,需要使用每个客户的配送详细信息充实订单请求消息,以便分派步骤包含取得成功所需的所有信息。此充实是通过调用客户信息系统在请求流中执行的,并且使用服务调用原语实现。分派系统在此处是作为调出节点实现的。(下载场景 2 文件。)

设计聚合

设计聚合的关键是将订单和分派系统实现为批处理系统,其中多个记录在同一请求消息中到达。不过,可以将客户信息系统实现为接收单个记录(从而引入对聚合的需求),并分别处理在请求消息中发生的每个重复元素,然后构建适合于分派系统的批处理消息。

要探索如何构建此基本场景,您必须先检查所使用的业务对象(请参见图 3 和图 4)。

图 3. 基本业务对象
基本业务对象
基本业务对象
图 4. 批处理业务对象
批处理业务对象
批处理业务对象

销售请求是一个由 batchID 标记的 SalesOrder 对象的批处理数组。

图 5. 请求对象
请求对象
请求对象
图 6. 响应对象
响应对象
响应对象

分派系统采用一批 DispatchOrder 对象,并返回关联的 DispatchStatus 对象。

流概述

如图 7 中所示,您可以看到流的每个部分,其中包括扇入和扇出原语的使用,以及总体流中所需的任何映射。

图 7. 批处理流
批处理流
批处理流

不过,在执行该流之前,您必须首先了解如何为此场景设计有用的共享上下文。

共享上下文

共享上下文的设计对成功的聚合设计至关重要,这个流的设计同样如此。目标是构建 DispatchOrder 对象的数组,这样可以将创建的消息发送到分派系统。每个 DispatchOrder 是使用初始 SalesOrder 中的数据创建的,并使用客户信息系统中的更多数据进行了增强。完成此操作后,您需要为所有这些个别订单构建聚合图片,方法是将其移入 DispatchOrder 对象的数组。这将为共享上下文得出一个明显的模式(请参见图 8)。

图 8. 用于批处理的共享上下文
用于批处理的共享上下文
用于批处理的共享上下文

在扇出的每个迭代中使用当前字段为相关的客户创建 DispatchOrder。然后将它追加到 aggregationStore,后者是最新的有效运行聚合总数。注意:这是在聚合中设计共享上下文使用情况的标准模式,本文不再介绍。

流详细信息

现在我们详细了解一下流,并从聚合原语扇出和扇入着手。在请求消息中,将扇出设置为遍历重复元素,在这种情况下,重复元素是 SalesOrders。同样,将扇入设置为在处理完所有的重复元素时完成(请参见图 9)。

图 9. 用于批处理的扇入详细信息
用于批处理的扇入详细信息
用于批处理的扇入详细信息

扇出后面是称为 setBatchID 的 Message Element Setter,使用它可以将 batchID 从请求消息复制到共享上下文(参见图 10)。

图 10. 设置 batchID 的 Message Element Setter
设置 batchID 的 Message Element Setter
设置 batchID 的 Message Element Setter

这只需为在流的结尾处创建分派消息而准备的种子上下文。

在调用客户信息系统之前,您必须将消息正文转换为正确的格式;使用称为 convertToCustomer 的 XSLT 原语可以做到这一点。这是将当前迭代的 customerID 复制到服务调用消息的简单转换。通过为上下文和标头创建内联映射并将 customerID 从 FanOutContext 发生字段简单地移动到输出消息正文中的 customerID 字段可以做到这一点。

图 11. 客户信息系统的转换
客户信息系统的转换
客户信息系统的转换

客户信息系统是使用称为 customer 的服务调用原语调用的,并返回在正文中包含有关客户详细信息的消息。需要将这些消息复制到共享上下文,在称为 storeResult 的 XSLT 原语中可以做到这一点。

图 12. 从客户信息系统存储响应的转换
从客户信息系统存储响应的转换
从客户信息系统存储响应的转换

请注意,在这一特定示例中,除从当前迭代的发生次数(即 itemID 和数量)复制的信息外,还可以从服务调用返回的客户详细信息构建当前迭代的输出。最初,此转换是通过为所有匹配字段创建内联映射构建的;不过,由于要专门复制到共享上下文的 current 对象,所以必须要记住删除此特定对象的内联映射,如图 13 所示。

图 13. 共享上下文的内联映射转换
共享上下文的内联映射转换
共享上下文的内联映射转换

现在,您从共享上下文的 current 对象中的当前迭代获取了完整结果,但是在开始下一个迭代之前,您必须对这些结果进行复制。因此,流中的下一个原语是称为 aggregateResult 的 Message Element Setter,它可以将 current 对象追加到共享上下文中的 aggregationStore

图 14. 将响应追加到 aggregationStore
将响应追加到 aggregationStore
将响应追加到 aggregationStore

现在已完成了每个元素所使用的聚合流。您需要做的事情是将 aggregationStore 转换为分派 Callout 节点的正确消息。这可以定义当所有迭代完成并且扇入触发其末端时发生的情况。您可以使用称为 convertToDispatch 的 XSLT 原语做到这一点。

图 15. aggregationStore 到请求的转换
aggregationStore 到请求的转换
aggregationStore 到请求的转换

正如您看到的,整个聚合流的关键之一是在 current 对象的每个迭代中构建所需的信息,然后将 current 追加到共享上下文中运行的 aggregationStore 中。

场景 3:需要嵌入聚合的批处理

此场景包括以前描述的两个示例的合并,并让来自多个来源的数据聚合嵌入到批处理聚合中。该场景是基于人员的简单申请,请求发送大量员工的工资单。后端服务由 Callout 节点表示,并需要员工的完全记录,而不是到达中介流的员工 ID。中介可以从两个来源(即人力资源系统和工资单系统)创建员工的这些记录。前者存储的信息涉及员工的姓名、地址和可享有的假期,而后者包含有关薪水和部门的信息。(下载场景 3 文件。)

设计聚合

该场景使用嵌入式聚合,因为它可以很好地映射为两个服务所需的、在中介流中调用的数据。换句话说,每次调用时,二者都期望单个数据记录,但是也允许完整的中介流批处理请求;这将使用重复的元素。通过查看中介流使用的数据对象可以很好地了解它(请参见图 16)。

图 16. 请求和响应业务对象
请求和响应业务对象
请求和响应业务对象

流的输入是员工标识符的数组,而输出是状态对象的数组,指示工资单处理是否成功。不过,从中介流中调用的每个服务只能处理单个记录,如图 17 所示。

图 17. 后端系统交互的业务对象
后端系统交互的业务对象
后端系统交互的业务对象

因此,外部聚合将迭代所有的重复元素,而内部聚合将构建从两个服务调用聚合的响应。对于前面的示例,当必须从重复元素创建响应时,共享上下文的规范对成功的流设计至关重要。在本例中,完成对两个服务的成功调用时,您需要将 HRRecordPayrollRecord 结果合并成单个的 EmployeeRecord。而且,此单个响应需要聚合成一组记录,以满足通过 Callout 节点调用的工资单服务的接口。因此,采用与前面描述的共享上下文相同的模式(请参见图 18)。

图 18. 后端系统交互的业务对象
后端系统交互的业务对象
后端系统交互的业务对象

使用当前字段为重复元素的每个实例创建单个记录,然后将其追加到 aggregationStore,后者是整个流的运行总数。

聚合并批处理数据后,将调用工资单服务,它将采用 EmployeeRecord 对象的数据,并返回 PayslipStatus 对象的数据(请参见图 19)。

图 19. 工资单服务业务对象
工资单服务业务对象
工资单服务业务对象

现在,让我们看一下图 20,并注意流的中间部分。

图 20. 嵌入式聚合流
嵌入式聚合流
嵌入式聚合流

此嵌入式聚合类似于前面描述的聚合,该聚合处理通过服务调用发送到两个系统的单个记录。此操作是由 XSLT 转换器-convertToHR 和 convertToPayroll 实现的,并获取 FanOutContext 中单个重复元素发生次数,将相关字段映射到最适合 Service Invoke 调用的消息。图 21 显示了包括此内容的流的各个部分。

图 21. 外部聚合拆分
外部聚合拆分
外部聚合拆分

您在上面看到的内容将重复触发,直到用尽所有重复的元素。图 22 显示了结果。

图 22. 外部聚合扇入
外部聚合扇入
外部聚合扇入

然后将共享上下文中的聚合结果转换为 Callout 节点的适当请求消息。

在前面场景中描述的 XSLT 映射中使用的所有策略在此处同样适用。

场景 4:嵌套式聚合

此场景基于超市公司,其中每个分店将多个进货订单发送给中心公司范围的系统,该系统可以将请求批处理为单个消息。此单个消息被交付给中介流,其中对于每个订单和每个分支,都应发生对订单系统的单独调用。(下载场景 4 文件。)

设计聚合

这里的关键是将订单系统实现为接收单个记录,而到中介模块的输入是嵌套式批处理消息。这将引入对聚合的需求,并且在这种情况需要使用嵌套聚合。因为批处理请求消息是以分店为基础重复的,然后以订单为基础重复。Orders 是分别发送的,并为稍后聚合为单个响应消息存储每个订单的响应,从而显示批处理请求中所有元素的状态。

要了解此基本场景是如何构建的,您必须首先检查所使用的业务对象(请参见图 23)。

图 23. 基本业务对象
基本业务对象
基本业务对象

GroupOrders 请求是由 ID 标记的 Outlets 的批处理数组,其本身是 Orders 的批处理数组。此嵌套式结构可以很好地适合嵌套式聚合结构,本文后面将介绍此内容。

图 24. 请求业务对象结构
请求业务对象结构
请求业务对象结构

GroupOrderResult 包含的嵌套式结构与该请求类似。GroupOrderResult 是由 ID 标记的 OutletResults 的批处理数组,其本身是 OrderResults 的批处理数组。

流概述

在本部分中,您会看到流的每个部分,其中包括每个扇入和扇出原语的使用,以及总体流中所需的任何映射(请参见图 25)。

图 25. 流概述
流概述
流概述

不过,在执行该流之前,您必须首先了解如何为此场景设计有用的共享上下文。

共享上下文

正如前面所述,共享上下文的设计对成功聚合设计至关重要,并且在嵌套式聚合中尤其重要。目标是将来自每个 Order 的响应存储到结构中,然后将其转换为单个响应消息。嵌套式聚合结构类似于俄罗斯套娃的型式,其中初始请求的范围是全公司,第一个聚合的范围是 Outlet,第二个聚合的范围是 Order(请参见图 26)。

图 26. 聚合结构
聚合结构

理解聚合结构有助于定义正确的共享上下文,因为通常存在一个映射。中介流中的逻辑模式为:

  • Outlet 基础上聚合。
    • Order 基础上聚合。
      • 调用 Order 服务。
      • 将当前的 Order 服务存储在当前的 Outlet 响应中。
    • 对每个与 Outlet 关联的 Order 进行重复。
    • 将当前的 Outlet 存储在公司范围的聚合存储中。
  • 对每个 Outlet 进行重复。
  • 将所有结果转换为响应消息。

这将提供对共享上下文的需要,以便能够存储全公司范围的响应、当前的 Outlet 响应和当前的 Order 响应。图 27 演示了具体内容。

图 27. 共享上下文业务对象
共享上下文业务对象
共享上下文业务对象

流详细信息

现在详细了解一下流,流划分为三个部分:

  • 公司范围
  • Outlet 范围
  • Order 范围
图 28. 流的公司范围部分
流的公司范围部分
流的公司范围部分

在公司范围(请参见图 28)中,将请求消息不加修改地传入 Outlet 聚合,因此,此阶段没有具体操作内容。退出 Outlet 聚合时,需要从共享上下文生成响应消息。您可以通过使用 XSLT 或 BOMapper 原语完成此任务。在本例中,已使用 XSLT 原语从共享上下文中的 aggregationStore 映射到响应正文,图 29 至 31 显示了此内容。

图 29. 到响应消息的转换
到响应消息的转换
到响应消息的转换
图 30. 响应消息:OutletResults 子映射
响应消息:OutletResults 子映射
响应消息:OutletResults 子映射
图 31. 响应消息:orderResults 子映射
响应消息:orderResults 子映射
响应消息:orderResults 子映射

在此阶段突出显示的重要方面是需要简单的转换。仔细考虑共享上下文结构可以获取简单的 XSLT,而无需任何自定义 XSLT。

图 32. 分店
分店
分店

分店范围为请求消息中提供的 Outlets 数组提供循环功能。将 OutletIterator(扇出原语)设置为在请求消息中迭代重复的 Outlet 元素。同样,将扇入设置为在处理了所有的重复元素时完成(请参见图 33)。

图 33. 用于分店聚合的扇入
用于分店聚合的扇入
用于分店聚合的扇入

扇出后面是称为 StoreOutletID 的 Message Element Setter,可以使用它将 ID 从请求消息复制到共享上下文(参见图 34)。

图 34. 存储分店 ID
存储分店 ID
存储分店 ID

这只是在 Order 聚合期间为了将当前的 Order 追加到分店而准备的种子上下文。

扇入后面是称为 StoreOutletResult 的 Message Element Setter,它通过追加选项将单个分店的所有结果追加到 aggregationStore(请参见图 35)。此外,应从共享上下文删除当前的 Outlet,以便为下一个迭代做好准备。

图 35. 将分店追加到 aggregationStore
将分店追加到 aggregationStore
将分店追加到 aggregationStore

Order 范围为请求消息中每个 OutletOrders 数组提供循环功能。将 OrderIterator(扇出原语)设置为迭代重复的 Orders(包含在第一个扇出原语的 FanOutContext 中)。

图 36. 订单聚合
订单聚合
订单聚合

同样,将扇入设置为在处理了所有的重复元素时完成(请参见图 37)。

图 37. 用于订单聚合的扇入
用户订单聚合的扇入
用户订单聚合的扇入

扇出后面是称为 TransformToOrder 的 XSLT,XSLT 可以将新的 FanOutContext(包含当前的 Order 元素)转换为 Order 服务的请求消息(请参见图 38)。

图 38. 到订单请求格式的转换
到订单请求格式的转换
到订单请求格式的转换

服务调用然后调用 Order 服务,在此之后,称为 StoreResult 的 Message Element Setter 将结果存储到共享上下文中的 currentOrder 元素中。

图 39. 将当前订单存储到共享上下文
将当前订单存储到共享上下文
将当前订单存储到共享上下文

需要将共享上下文中存储的 Order 结果存储在 Outlet 结果 Order 数组中。因此,称为 AppendStore 的另一个 Message Element Setter 将 currentOrder 复制到 OutletOrder 结果数组中。

图 40. 将当前订单追加到共享上下文
将当前订单追加到共享上下文
将当前订单追加到共享上下文

不需要从这一聚合级别的共享上下文删除 currentOrder 对象,因为在下一个迭代过程中将重写该值。

正如您看到的,如共享上下文那样,聚合功能可直接映射到入站数据的结构。在本例中,重复的元素中包含重复元素,所以聚合是双重的和嵌套的。

其他考虑事项

了解聚合中的有用模式后,在使用 WebSphere Enterprise Service Bus V6.1 中的聚合设计中介流时,还应考虑其他要点。

  • 扇入的超时属性:扇入原语具有相关的超时属性,需要对该属性进行一些说明,以便您可以成功地使用它。超时期间从关联扇出第一次触发输出末端时开始。如果消息在此超时期间之后到达扇入末端,则认为其迟到,扇入将触发其未完成的末端。如果中介原语的行为错误(例如,DB 查询元素发生死锁),则流的执行也会发生死锁;通过扇入超时不能缓解此问题。因此,应将超时功能视为捕获最新消息的方法,而不是在指定期间后终止聚合的方法。
  • SMO 正文注意事项:中介流编程模型保证使用共享上下文的显式异常可隔离单独的流分支,并获取自已的 SMO。因此, 应该使用消息的正文存储扇出/扇入边界外部所需的任何数据;它们是不可信赖的。此方法中使用的任何数据应存储在为此用途提供的共享上下文中。
  • 在聚合中使用响应流:即使使用服务调用原语提供支持增强的流内服务调用,但响应流仍扮演中介中的关键角色。设计易于维护的中介并在整个中介流中提供合理的数据流非常重要。如果中介流中存在逻辑上的单一调用,则应合理使用请求流中的调出节点,然后将响应流用于任何其他中介处理。前面描述的场景中演示了响应流的使用情况。

结束语

本文向您介绍了一些常见的聚合使用模式,并探索了如何使用 WebSphere Enterprise Service Bus V6.1 实现这些模式。本文是由三篇文章组成的系列文章的最后一篇文章,完成本文后,您应了解了组成聚合场景的基本构建块,以及如何使用这些构建块创建实际的业务流程。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, WebSphere
ArticleID=314137
ArticleTitle=IBM WebSphere Enterprise Service Bus V6.1 中的聚合功能,第 3 部分: 最佳实践和聚合模式
publish-date=06162008