摘要
Java 业务集成(JBI)规范,即所谓的 JSR 208,描述了企业服务总线(ESB)及其交互。这些交互是根据插入到软件总线以交换信息的组件来描述的。乍一看,这种结构似乎要创建一个所有组件都位于相同进程或虚拟机内的系统。OpenESB 是 JBI 的一个开源实现,当其运行在 GlassFish 应用服务器上时,它包含了一个使用了集群 JBI 实例的同构拓扑选项。这使得,通过增加集群实例的数量,就能实现伸缩性和负载均衡。最近,一种名为代理绑定(Proxy Binding)的组件的原型实现,允许使用异构拓扑方式透明地把 OpenESB 实例链接在一起,这和扩展网络上的总线非常相似。这篇文章将描述和对比在 OpenESB 环境下应用的两种不同分布式访问方式的优缺点,同时在文章结尾展示了二者是如何进行互补的。
引言
JBI 规范常常被认为是就单 Java 虚拟机实例(JVM)而言的。该规范并没有明确指出分布式版本的工作方式。这不应该扼杀我们探索新世界的兴趣!事实上,两种不同风格的解决方案,在这次讨论中我们将称其为拓扑,自规范通过之后就已经出现了。这些解决方案仅要求在高级管理功能和一些低级信息钩子(Hook)之上稍加补足。随着这些改变,这两种不同的拓扑已经以近乎透明的方式被引入了。
以下小节的目标是:通过例子介绍这两种不同的拓扑和一个组合拓扑;讨论每种拓扑的详细内容;讨论 JBI 组件的实现细节在拓扑决策中发挥的作用。本文的整体目标是要表明,这两种不同的拓扑可以单独使用,但是如果将二者结合起来,将会获得更大的益处。另一个值得注意的是,文章强调了在这种组合上构建的组件也能以一种广泛的方式为整体功能做出贡献。
拓扑概览
我们将从两种不同拓扑的示例开始,接着介绍一个组合的示例。由于历史原因,这两种不同的拓扑被称为同构和异构。这些示例来自运行在 GlassFish 环境下的 OpenESB。
同构拓扑
顾名思义,同构拓扑就是创建拥有相同 JBI 内容的 OpenESB 实例。
图例 1:同构拓扑 y
异构拓扑
同理,异构拓扑就是创建含有不同 JBI 内容的 OpenESB 实例。基本应用流程是一样的:HTTP 请求最终将导致 BPEL 流程的调用和响应。这个拓扑由一种被称为代理绑定(如图例中的 PB)的特殊组件所支持。
图例 2:异构拓扑
组合拓扑
这个示例是前两个示例的混合体,其中一个额外的 JBI 实例包含了不同的服务。
图例 3:组合拓扑
OpenESB 的拓扑细节
在决定选择哪种拓扑时,有许多因素需要考虑。以 OpenESB 为例,在第一次实现时,选择同构,这是因为它与用作主要托管环境的 GlassFish 所提供的功能相匹配。异构事实上已经有了第一个原型,同时这个原型现在还可用。实现顺序并不是真正的问题,两种方式都可以单独使用,而且结合使用时则是互补的。
同构
同构拓扑会创建拥有相同 JBI 内容的 OpenESB 实例。JBI 内容包含了一组构成一个或多个组合应用的组件,服务程序集和配置信息。这些映射到底层 GlassFish 集群实例的 JBI 实例能够按需被创建、销毁、启动和停止。集群实例可以由每台机器上的一个或多个实例创建,这些实例由单个操作系统映像来管理。这种灵活性可以更好地使用 cpu 资源和更高效地使用多线程处理器,这些资源通常是在面向 Web 使用者的地方发挥作用。在从包含整个集群配置的中心管理服务器(CAS)启动期间,新创建的实例或者那些执行集群变更之时还未运行的实例将同步它们的配置。这就确保了所有集群实例功能保持一致。
伸缩性
这种拓扑常常被用来水平伸缩拥有大量输入的面向 Web 的服务。易于适应负载变化是其主要优点。除此之外,GlassFish 还包含了一个软件 HTTP 负载均衡器,这个均衡器可以放在集群实例场的前端,对这种拓扑用法进行补充。
管理
这种拓扑直接得到了 GlassFish 应用服务器的支持。OpenESB 包含了管理方面的提升,即先在 CAS 本地记录配置变更,然后将变更传播给活动实例。这些提升使得 JBI 管理操作可以针对单个特定实例、实例集或者所有实例。因此,严格的同构不是一个强制要求,而是一个典型应用。
示例细节
前面的同构图例描述了一个简单的两个实例集群。每个集群实例包含两个组件,即 HTTP 面向 Web 绑定组件和 BEPL 服务引擎。一般应用很可能包含许多其他组件(数据库访问,E-Mail 协议,或者甚至于是像 HL7 或 SAP 这样非常专业的协议)。HTTP 和 BPEL 组合是执行有效工作量的最小配置。集群前端是一个软件 HTTP 负载均衡器。图中还画出了集群管理服务器。
需要注意的几个重点:
- 每个集群实例通常都是相同的。在集群实例部署于同一个操作系统映像和网络接口的情况下,可能要配置每个 HTTP 实例使用不同的端口号。
- 集群内部的服务端点是重复的,但是它们的作用域是针对每个实例而言的。
- 集群实例的数量可以随时间变化。正是这个特性使得该拓扑可以水平伸缩。它们可以在同一物理机器或不同机器上,这两种情况的混合也是允许的。通常情况下,集群实例的物理位置是在一起的。
异构
异构拓扑创建的 OpenESB 实例可以包含不同的 JBI 内容。这个拓扑由一个被称为代理绑定的特殊组件支持。代理绑定允许驻留一个实例上的组件服务被另一个实例上的组件使用。代理绑定运行在每个期望成为松耦合关系中一员的实例上。组件只使用那些它们被配置使用的相同的服务端点名。每个代理绑定实例都构建和维护一个目录,该目录负责映射由实例提供的服务。如果多个实例输出相同的服务,缺省情况下,代理绑定会在这些提供者实例间负载均衡请求。目前,异构系统是作为独立系统来管理的。
代理绑定处理的主要问题是感知(Awareness)和传输。感知是代理绑定的一种能力,这种能力可用来通告其共享服务的意愿并用来定位具有同样意愿的其他代理绑定。一旦感知被解析,接下来的问题就是在实例间如何传输包含规范消息(NormalizedMessage,NM)的 JBI 消息交换(MessageExchange,ME)。
感知
感知是一个已被长期详细研究的问题。JXTA 和 JGroups 是已经演变成优秀底层解决方案的项目。Shoal/GMS 是一个较新的项目,基于一系列底层技术(包括 JXTA 和 JGroups)提供了一种高层解决方案。Shoal 强大但又不失于易用性,该项目既可以独立使用,又可以作为 GlassFish 应用服务器提供的服务使用。GlassFish 应用服务器本身使用 Shoal 去管理集群感知,在集群实例失效时为自动 XA 事务恢复提供支持,并为 EJB 提供分布式状态缓存。
代理绑定使用 Shoal 作为其缺省感知机制。这种感知机制被有意识地设计成了具有可插拔能力。这使得未来可以创建和使用不同的感知机制。
传输
有大量的传输协议可供人们从中选择。缺省情况下,代理绑定使用 Shoal 作其传输。Shoal 提供了消息传递原语,它支持:点对点、一对多和一对所有(one-to-all)的寻址方式。对于服务端点变更相关的消息,代理绑定使用一对所有的方式;对于组件之间包含 ME+NM 内容的消息,使用点对点的方式。其他各种传输协议正在接受调查。调查涉及两个主题: 性能和可靠性。
传输性能按“消息 / 秒”和“总字节吞吐量”进行度量。HTTP 是显而易见的候选。最终的性能可能来自底层 TCP 或 UDP 传输的某些形式。拥有针对安全性和完整性的 SSL/TLS 的传输层将满足大多数需求。如果可靠性不是必需的或者可靠性是以不同方式进行维护,那么基本要求就是为使用提供一个高容量的传输。
传输可靠性有类似的性能问题,但是其令人感兴趣的好处是一次且仅一次传送。这一般是采用某种形式的持久化存储来实现的。在分布式系统中最常见的使用方式是消息队列系统。消息队列系统通过允许消息一直存储到接收系统变为可用为止,隐藏了可用性问题。这种类型的传输可用性可以大大增强部署在该基础架构上的应用整体能力。GlassFish 包含了 JMS 消息队列系统。这使得 JMS 自然成为一个目标传输选择。由于 JMS 是 XA 事务感知的,这将让代理绑定共同分担事务。这将极大增强它的能力。除此之外,拥有 JMS 的效果类似于结合两个组件间的 JMS 绑定组件。在传输部分这样做最终会节省一些开销。
在传输层包含选择或者选项有助于将应用需求映射到平台能力。基于在 ME 中标注的服务质量来选择传输类型也是可能的。从代理绑定角度看,这只意味着同时支持实例间的多个传输,并基于任何可用的信息去选择一个使用。
示例细节
前面的异构图例描述了三个独立的 OpenESB 实例。它可以实现与同构示例相同的应用。这个示例中的不同点在于:这里只有一个面向 Web 的 HTTP 绑定组件和两个不同的 BPEL 服务引擎组件,每个引擎组件执行相同的服务(epB)。
需要注意的几个重点:
- 每个实例包含一个代理绑定组件,该组件用来维护分布式的感知和透明地执行实例间任何消息传递。
- 如果实例 1 中的 NMR 获得了指向端点 epB 的消息,它可以在实例 2 或实例 3 之间选择路由。缺省情况下,该 NMR 会马上在可用实例间进行负载均衡。因而,决策是基于活动 ME 数目最少的实例做出的。
- 异构实例有一组位置选项。它们可以是在相同的操作系统映像上,或者是完全不同的操作系统上,也可以是两者的结合。
组合
前面的组合图例是前两个实例的混合体。一开始是前端有一个负载均衡器的面向 Web 的集群实例。每个集群实例也是连接第三个实例的异构分布式系统中的一员。
需要注意的几个重点:
- 同构拓扑和异构拓扑可以混合使用。代理绑定组件控制分组。理论上,一个实例可以属于多个组(这还没有被正式地测试)。
- 选择混合拓扑有很多理由:第三方软件的许可限制,访问遗留系统,轻负荷,组件架构约束(即,在一个集群中效果不好),地理分隔等等。
- 如果 NMR 获得对 BPEL 资源的 HTTP 请求,这时在本地 BPEL 实例或远程 BPEL 实例之间需要进行选择。缺省情况下,本地实例会比远程实例优先选择,该决策的作出并不涉及考虑代理绑定组件。如果本地 BPEL 因异常失效了,请求将会被路由到剩下的实例上。
组件质量特性
规范消息路由器(NMR)是 ESB 的一部分,所有组件将使用它来发送和接收组件间消息。NMR 专注于在组件间快速交换消息。一个 ME 要么执行完成要么执行终止。该机制并没有被设计成可持久化或是可恢复的。一个 ME 可以包含被用来使 ME 活动具有事务性的 XA 事务信息。OpenESB 中可以使用的许多组件都是使用中间层来构造的,该层在简单的原语基础之上添加了一些系统质量特性。可靠性质量特性是此次讨论的重中之重。最受人关注的可靠性质量特性包括:唯一标识符、重试、重复检测和事务性交换。
大多数组件都支持“至少一次(at-least-once)”的消息传递,它是通过混合使用前 3 个质量特性来实现的。失败的消息会被不断重试直到返回一个响应或者达到阈值。唯一标识符会附着在 ME 上,以帮助接收组件检测和拒绝重复请求的再次出现。这一系列的活动提供了“至少一次”的服务水平。.
某些组件支持事务性活动。这一般是为组件实现而拥有持久化存储并额外获得 XA 事务支持的一个副产品。这让事务可以包含跨两个组件的 ME 动作。在单事务内包含多个 ME 可以把事务扩展到多个组件。但要是事务跨越的广度太大或者时间太长的话,事情往往会变得难以控制。使用事务的 ME 提供了“一次且仅一次”的服务水平。
有些组件使用持久化的共享状态来支持在不同集群实例上同时执行相同组件。这个共享状态还可被用来将失效实例遗留下来的工作转移到另一个仍在正常运行的实例上。从可靠性的观点看,就灵活性而言,这种类型的组件是最先进的。JavaEE 引擎和 BPEL 引擎是这种类别中的主要案例,伴随对 JMS 绑定的极少扩展。
示例
下图显示了两个实例的集群和运行于集群上的组件集合,这些组件要么是可以被忽视的,要么是可以被感知到的。
图例 4:组件质量特性
需要注意的几个重点:
- XLST 是一个无状态组件,仅仅对 XML 文档应用 XSLT 转换。这个操作可被重试,不会带来受任何实质性影响。
- 企业 Java Beans(EJB)将它们的状态持久化到数据库和缓存到分布式内存缓存中。支持多实例访问。
- JMS 在某种持久化存储中维护它的消息队列,典型的是基于文件或者存储在数据库中。集群实例由一个处理多个实例访问的单独进程来管理。
- BPEL 能够被配置成将它的流程内部状态持久化到数据库中。失效的 BPEL 实例可以在不同集群实例上被恢复,处理中的工作会被作为引用被处理流程的活动迁移到一个可用的 BPEL 实例上。
- EJB、JMS 和 BPEL 都支持 XA 事务。这使得原子活动能够作为 JBI 交换的一部分被执行。例如,事务将从 JMS 队列中获取的内容打包,之后发送给 BEPL 引擎,同时作为一个流程变量被持久化。
总结
这篇文章的目标是为了说明如何将 OpenESB 从单 JVM 的世界中解放出来,它涉及了支持分布式功能的两种不同拓扑。这两种拓扑是互补的。
就分布式这个更宽广的领域而言,消息质量特性甚至更为重要。基础 NMR 使用内存中的消息传递结构。建立在 NMR 层之上的组件添加了外部协议。这些外部协议中的某些,比如 JMS,非常适合于以一种比组件自身所支持的要可靠得多的方式来连接组件。将 JMS 作为分布式传输协议使用将实现一个具有内聚性的系统。让消息参与事务的附加能力给解决方案增加了可用性。
除此之外,我们还说明了组件也为最终结果的整体功能作出了巨大贡献。让组件实现一组可配置的系统质量特性公共集合,将使组合应用设计者自由地探索更大范围的解决方案。大不见得就是好,但是在处理现有系统的集成时这是非常有帮助的。集成是导致 JBI 产生的初始念头,但在其他情况下,其效用也将被证明。
参考资料
- JBI - http://jcp.org/en/jsr/detail?id=208
- OpenESB - https://open-esb.dev.java.net/
- GlassFish - https://glassfish.dev.java.net/
- Shoal/GMS - https://shoal.dev.java.net/
- JXTA - https://jxta.dev.java.net/
- JGroups - http://www.jgroups.org/javagroupsnew/docs/index.html
- BPEL SE - http://wiki.open-esb.java.net/Wiki.jsp?page=BPELSE
- JMS BC - http://wiki.open-esb.java.net/Wiki.jsp?page=JMSBC
- Proxy BC - http://wiki.open-esb.java.net/Wiki.jsp?page=FujiDJBI <
作者简介
Derek Frankforth 是 Sun 公司的一名 SOA/BI 高级工程师,他帮助定义了 JBI 和实现参考实现、开发 OpenESB 和 GlassFish ESB,现在正从事 Fuji 项目。他是因 Sun 收购 Forte 而加入到 Sun 的,在 Forte 工作期间,他的工作重心是核心和分布式运行时。在那之前,他将 Ingres 数据库系统带入到了商业领域。
查看英文原文: Distributed JBI 。
感谢黄璜对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论