这篇由两部分组成的文章将演示各种医疗保健相关的服务通过一个服务总线进行聚合,我称之为(可能不够准确)Healthcare Service Bus (HSB)。在第 1 部分中,我将介绍一个用例场景,其中为病人服务的各种应用程序需要连接到 HSB,我将解释 HSB 应当提供的特性。接下来,我将介绍 Java Business Integration (JBI) 架构,它用于构建 HSB。按照以下顺序了解发生在 JBI 服务器内部的事件,您将了解到 JBI 如何在内部用于业务集成,以及其组件如何与外部应用程序协同。第 1 部分的最后一章将提供配置实例,它将演示如何控制 JBI 组件行为,以使其用于医疗保健。在第 2 部分中,您将学习如何使用开源 JBI 实现 (Apache ServiceMix)现有的功能以及通过实现 ServiceMix 新功能集成医疗保健服务。
HSB 集成了大量医疗保健相关的服务。想象一下需要救命的紧急病人的需求,包括输血、紧急处方和放射检查。
当病人到达医疗机构,主治医生使用服务总线通过病人手机上运行的应用程序查看过敏史。医生还可将对病人最初情况的观察输入到连接到总线的医疗处方应用程序中。医生的观察通过服务总线传送到托管病人所在保险公司门户网站的 Web 服务器。
医生然后在相同的处方应用程序中开具输血处方。然后处方自动通过服务总线不仅传送到血库,还传送到捐赠组织应用程序中,它将向那些血液样本预先与病人配型成功的捐赠者发送短信。对捐赠组织应用程序的需求也通过服务总线传送。
医生还会开具紧急药物和放射检查处方,这些也输入同样的处方应用程序。处方应用程序通过总线发送处方到医疗机构内部的药房和放射科。
您可以看到在该用例中,HSB 允许各种应用程序相互连通,相互操作,从而聚合服务。应用程序的两种主要类型 — 服务使用者和服务提供者 — 连接到 HSB。向 HSB 发送输血需求的处方应用程序作为服务使用者(请求或使用服务的应用程序)。向潜在血液捐赠者发送短信的捐赠组织应用程序作为服务提供者(提供所请求服务的应用程序)。相互连通 和 相互操作 是不同的需求,它们共同提供了服务聚合。相互连通 意思是服务提供者和服务使用者有一种通用方式可以连接(到达)对方,从而可以 相互操作 对方(交互信息和消息)。HSB 使用通用的 XML 格式相互交换消息。
图 1 显示服务提供者和服务使用者连接到 HSB:
请注意 图 1 显示三个服务提供者连接到 HSB:Insurance Company Portal、Donor Group 和 Radiology Department 应用程序。HSB 应当能将服务使用者连接到内部和外部的服务提供者,以便它们能相互操作。在 图 1 中,Radiology Department 应用程序在医疗机构内部;Donor Group 和 Insurance Company Portal 应用程序在医疗机构外部。
为了确保相互连通,HSB 必须:
- 记录所有连接的服务提供者,以便它能将服务使用请求从服务使用者路由到正确的服务提供者。
- 提供一种标准机制,让服务提供者和服务使用者通过服务总线相互对话。
- 允许其他 HSB 与其相互连接。
HSB 的相互连接是个有趣的应用程序,其架构如图 2 所示:
图 2 显示了两个不同医疗机构的 HSB。其中一个机构有个 Blood Bank 应用程序,Prescription 应用程序可以通过两个 HSB 的互联进行调用。
将 XML 用于医疗保健服务互操作有两种方式:
- Web 服务:基于 Web Services Description Language (WSDL) 和 Simple Object Access Protocol (SOAP) 的 Web 服务在很多行业(包括医疗保健)中经常使用。WSDL 使用 XML 来定义服务;即,定义服务提供的接口。SOAP 是基于 XML 的协议,用于在服务提供者和使用者之间提供实际的消息交换(查看 参考资料 中更多关于 WSDL 和 SOAP 的信息)。HSB 应该能够与任何使用 WSDL 和 SOAP 的医疗保健应用程序相互连接。
- Health Level 7:Health Level 7 (HL7) 是一组通用的医疗保健标准,它定义了很多用于指定医疗保健信息的数据结构,例如医疗记录、处方、病人出院摘要。(早期的 HL7 版本 1 和 2 基于 ASCII。最新的 HL7 版本 3.X 使用 XML 作为定义消息结构的数据格式。)
我将演示的 HSB 能与使用 WSDL、SOAP 和 HL7 标准的医疗保健应用程序相互连接。
这样的用于相互操作和相互连接的一般架构通常是指 Enterprise Service Bus (ESB),它能够:
- 基于 SOA。
- 允许服务提供者和使用者使用 WSDL 和 SOAP。
- 具备可扩展性和灵活性,它允许服务提供者和使用者使用特定行业基于 XML 的标准,如 HL7。
ESB 不是新构想。目前有几种 ESB 实现。(查看 参考资料 中常用开源 ESB 的链接。)这意味着不必从头开始构建 HSB。可以配置现有的 ESB 用于医疗保健。
JBI 规范定义了标准的 Java 业务集成环境。JBI 提供了我所讲述的所有 ESB 特性,因此我将用它来构建 HSB。
有几个 JBI 实现可用,包括流行的来自于 Apache 的称为 ServiceMix 的开源实现。该系列其余部分关于使用 JBI 以及配置 ServiceMix 来构建 HSB。
图 3 显示 JBI 如何用作 HSB:
在 图 3 中可以看到 JBI 有三个主要部件,是 Binding Components (BCs)、Service Engines (SEs) 和 Normalized Message Router (NMR)。
我将借助当 Prescription 应用程序(服务使用者)连接到 Donor Group 应用程序(服务提供者)时所发生的一个事件序列(如图 4 所示)来解释 JBI 组件的工作方式:
图 4 中出现的序列如下:
第 1 步:Prescription 应用程序(服务使用者)连接到 JBI 并要求 Donor Group 应用程序(位于 JBI 环境外的服务提供者)提供的服务。
第 2 步:JBI 环境将服务请求发送到合适的 BC — 它接收来自 Prescription 应用程序的所有服务。每一个与 JBI 协同工作的服务使用者或提供者在 JBI 环境中都有一个专用 BC。
第 3 步:BC 将服务调用请求转换成 JBI 规范定义的规范化格式。定义规范化格式的目的是允许 BC 之间相互操作。所有 JBI BC 都理解规范化格式。每个 BC 还理解 BC 所附属的服务提供者或使用者的格式。换句话说, JBI 的规范化特性就是所有 BC 将从各自的消息使用者或提供者接收到的消息转换成通用格式。
第 4 步:Prescription 应用程序的 BC 将规范化信息移交给 图 4 中所示的 NMR。整个 JBI 环境包含一个 NMR。
第 5 步:NMR 的工作是从 BC 接收规范化信息,确认目标服务提供者,并传送(路由)规范化消息到另一个目标服务 BC。在这一步,NMR 将规范化消息发送到连接 Donor Group 应用程序的 BC。
第 6 步: Donor Group 应用程序的 BC 对规范化消息解除规范化,从而将它转换成 Donor Group 应用程序可以理解的格式。
第 7 步: BC 将解除规范化的消息移交给 Donor Group 应用程序。
事件序列揭示了关于 JBI 的简单的两点:
- JBI 运行基于 规范化消息路由 的构想。每个消息被 BC 规范化,并移交到 NMR。NMR 将消息路由到另一个 BC,它将消息解除规范化,转换成目标服务提供者能理解的格式。
- 规范化消息路由机制提供了解耦 结构。解耦意味着服务提供者和使用者只通过 NMR 机制进行交互。它们不直接交互。
这种解耦架构的主要好处就是只需实现一次特定的数据格式或标准,以 BC 的形式。以后,所有根据特定格式提供服务的服务提供者只要简单使用 BC 实例来集成到 JBI 环境。
例如,如果必须要集成 HL7 到 JBI,就需要一个理解 HL7 的 BC。如果有 HL7 BC,可以将任何 HL7 服务集成到 JBI,从而形成 HSB。
本系列的第 2 部分将给出构建基于 HL7 的 BC 和将 HL7 集成到 JBI 的实际步骤。但是现在,还有更多关于 JBI 的内容要学习。
此处的论述以及 图 4 演示了服务使用者和处于 JBI 环境外的服务提供者的通信。
回想在一下本文开头的用例中,Prescription 应用程序还向内部放射科发送消息。这意味着 JBI 环境还应当能托管 Radiology Department 应用程序作为内部服务。JBI 中的内部服务作为 SE。
SE 和 BC 基本一样,只多出一个特性:SE 还包含内部服务(例如,Radiology Department 应用程序)的业务逻辑。BC 和 SE 都连接到 JBI 的 NMR,如 图 3 所示,我在图 5 中做了些修改,以便演示作为 SE 的 Radiology Department 应用程序:
图 5. 作为 SE 的 Radiology Department 应用程序
访问内部服务(即,SE)的事件序列如图 6 所示:
图 6 的事件序列表示出了当服务使用者如 Prescription 应用程序发送消息给 Radiology Department 应用程序(内部服务提供者)所发生的事件:
第 1 步:Prescription 应用程序(服务使用者)连接到 JBI 并要求 Radiology Department 提供服务。
第 2 步:JBI 环境将服务请求发送到 Prescription 应用程序的 BC。
第 3 步:BC 将服务调用请求转换成规范化消息。
第 4 步:BC 将规范化消息移交给 NMR。
第 5 步:NMR 将规范消息发送到 Radiology Department 应用程序(SE)。
第 6 步:SE 在内部将消息解除规范化,并调用所需的业务逻辑。
事件序列 — 与 图 4 中演示的事件序列很像 — 显示了 SE 包含 BC 的功能和服务提供程序的业务逻辑。
似乎 SE 不必要混合两种不同的东西(BC的功能和业务逻辑)。在第 2 部分中,我将向你演示如何在现有 BC 之上构建内部服务的业务逻辑,而不需要混在一起。
还回到 图 2,其中我演示了 HSB 的相互连接。这种相互连接可以通过 JBI 实现,如图 7 所示:
请注意 图 7 中显示了不同的应用程序连接到两个独立的 JBI 环境。当 Prescription 应用程序(在 图 7 中显示连接到第一个 JBI)发送消息到 Blood Bank 应用程序(位于第二个 JBI 环境的服务提供者)会按照顺序发生以下事件:
第 1 步:Prescription 应用程序(服务使用者)连接到第一个 JBI 并要求 Blood Bank 应用程序提供服务。
第 2 步:第一个 JBI 环境发送请求到 Prescription 应用程序的 BC。
第 3 步:Prescription 应用程序的 BC 将服务调用请求转换成规范化消息。
第 4 步:Prescription 应用程序将规范化消息移交给 NMR。
第 5 步:NMR 将规范化消息发送给连接到第二个 JBI 环境的 BC。
第 6 步:连接到第二个 JBI 环境的 BC 将消息解除规范化。
第 7 步:连接到第二个 JBI 环境的 BC 将解除规范化的消息移交给第二个 JBI 环境。
第 8 步:第二个 JBI 环境接收请求,并将它发送到连接第一个 JBI 环境的 BC。这意味着两个 JBI 环境通过合适的 BC 相互连接。
第 9 步:连接第一个 JBI 环境的 BC 将把服务调用请求转换成规范化消息。
第 10 步:连接第一个 JBI 环境的 BC 将规范化消息移交给 NMR。
第 11 步:NMR 发送规范化消息给 Blood Bank 应用程序(SE)。
第 12 步:SE 在内部将消息解除规范化并调用所需业务逻辑。
前面的讲解及图 4、5、6 和 7 演示了一种单向通信 — 从服务使用者发送消息给服务提供者。服务提供者(例如 Donor Group 应用程序)不发送返回信息。JBI 文档将这种类型消息传递称为 in-only 消息交换。in-only 消息交换模式适用于 Donor Group 应用程序这样的服务,它只要将单向消息送到组织中。不需要返回信息到请求的 Prescription 应用程序中。
其他一些服务(例如 Insurance Company Portal 应用程序)也许会被要求返回响应。这种请求-响应消息模式在 JBI 中也是允许的,被称为 in-out。您也许会猜到,响应也是以同样方式通过 JBI 环境 — BC、SE 和 NMR — 从服务提供者发出,最终到达服务使用者。因此我不再讲述 in-out 消息传送的事件顺序。
JBI 规范提供了详细的 XML 模式,它用于定义所希望在 JBI 中托管的所有 BC 和 SE 的行为。现在我将描述如何能使用 JBI 模式来配置 JBI 实现作为 HSB。
本文中不会讨论整个 JBI 模式。我将重点讲解重要的 JBI 标记,称为 <component>
。第 2 部分将会演示更多 JBI 模式标记的使用。
<component>
标记定义 JBI 组件。组件可以是 BC 或 SE。清单 1 显示 Prescription 应用程序的 BC 的 MXL 配置是什么样子:
清单 1. prescription 应用程序的 BC 的 XML 配置
<?xml version="1.0" encoding="UTF-8"?> <jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0"> <component type="binding-component" component-class-loader-delegation="parent-first" bootstrap-class-loader-delegation="parent-first"> <identification> <name>Prescription-Application</name> <description>Binding Component for the prescription application</description> </identification> <component-class-name> org.apache.servicemix.cxfbc.CxfBcComponent </component-class- <component-class-path> <path-element>lib/servicemix-cxf-bc-2009.01.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </component-class-path> <bootstrap-class-name> org.apache.servicemix.common.DefaultBootstrap </bootstrap-class-name> <bootstrap-class-path> <path-element>lib/servicemix-cxf-bc-2009.01.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </bootstrap-class-path> </component> </jbi> |
可以看到 清单 1 包含 <component>
标记,它有一个 type
属性,五个子标记,名为 <identification>
、<component-class-name>
、<component-class-path>
、<bootstrap-class-name>
和 <bootstrap-class-path>
。
type
属性指定组件是 BC 还是 SE。清单 1 配置的是 Prescription 应用程序的 BC,因此 type
属性值应当是 binding-component
。
清单 1 中的 <identification> 标记提供了 BC 的名称和描述。
<component-class-name>
标记指定实现 BC 所需的逻辑的 Java 类。JBI 规范有个标准接口名为 javax.jbi.component.Component
,所有 BC 都应当实现。本系列中我将使用 Apache ServiceMix 来演示 HSB 如何工作。ServiceMix 提供 BC,它可用于与使用基于 SOAP 的 Web 服务的服务使用者协同工作。实现这个 BC 的逻辑的类名为 org.apache.servicemix.cxfbc.CxfBcComponent
。在第 2 部分中,我将用这个类来演示 Prescription 应用程序如何与 JBI 一起工作。这就是在 清单 1 中包含org.apache.servicemix.cxfbc.CxfBcComponent
作为包装在 <component-class-name>
标记中的组件类的名称的原因。
现在看看 清单 1 中的 <component-class-path>
标记。它有两个子标记,名为 <path-element>
。 这些标记指定执行组件类需要的所有 JAR 文件的路径。这意味着 <component-class-name>
和 <component-class-path>
标记形成一对,来指定 BC 的 Java 类的名称以及用于执行 Java 类的完整类路径。
清单 1 还包含另一对标记,名为 <bootstrap-class-name>
和 <bootstrap-class-path>
。这一对与前一对 <component-class-name>
和<component-class-path>
标记相似。引导对指定实现引导 BC 的 Java 类的名称和类路径。
引导意味着让 BC 投入服务。在启动 JBI 服务器(ServiceMix)时开始引导。引导类包含所有将 BC 激活、启动和运行的逻辑。
现在看看清单 2 中的 <component>
标记,它表示外部服务提供者,如 Donor Group 应用程序。清单 2 和 清单 1 有一模一样的结构,因此无需多解释。
<?xml version="1.0" encoding="UTF-8"?> <jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0"> <component type="binding-component" component-class-loader-delegation="parent-first" bootstrap-class-loader-delegation="parent-first"> <identification> <name>Donor-Group-Application</name> <description>Binding Component for the Donor Group application</description> </identification> <component-class-name> org.apache.servicemix.cxfbc.CxfBcComponent </component-class- <component-class-path> <path-element>lib/servicemix-cxf-bc-2009.01.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </component-class-path> <bootstrap-class-name> org.apache.servicemix.common.DefaultBootstrap </bootstrap-class-name> <bootstrap-class-path> <path-element>lib/servicemix-cxf-bc-2009.01.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </bootstrap-class-path> </component> </jbi> |
清单 3 显示另一个 <component>
标记,它描述了如何配置 SE — 一个内部服务提供者(例如 Radiology Department 应用程序):
<?xml version="1.0" encoding="UTF-8"?> <jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0"> <component type="service-engine" component-class-loader-delegation="parent-first" bootstrap-class-loader-delegation="parent-first"> <identification> <name>Radiology-Department-Application</name> <description>Radiology Department Service Application</description> </identification> <component-class-name> org.hsb.radiology.RadiologyBean</component-class-name> <component-class-path> <path-element>lib/radiology-bean.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </component-class-path> <bootstrap-class-name> org.apache.servicemix.common.DefaultBootstrap </bootstrap-class-name> <bootstrap-class-path> <path-element>lib/ radiology-bean.jar</path-element> <path-element>lib/geronimo-annotation_1.0_spec-1.1.1.jar</path-element> <!-- other path-element tags --> </bootstrap-class-path> </component> </jbi> |
如果将 清单 3 与 清单 2 对比,将会看到两个主要区别:type
属性值在 清单 2 中是 binding-component
(因为是表示外部服务提供者的 BC),而 清单 3 中的 type
属性值(以粗体显示)是 service-engine
,因为它是表示内部服务的 SE。
您已看到如何使用 JBI 的 <component>
标记来配置 BC 和 SE。JBI 还提供重用机制来构建可用于各种场景的通用 BC。
JBI 实现如 ServiceMix 提供预置的 BC,它与实现捆绑在一起。您甚至不需要为这样的 BC 配置 <component>
标记,因为实现已经知道这种 BC 的类和其他细节。
第 2 部分将各部分组合起来,演示如何使用 ServiceMix 现有功能以及构建和配置新功能 — 即,实现和配置 BC 或 SE — 来集成医疗保健服务。