技术开发 频道

用 J2EE 1.2 部署多个应用程序

  在这第三个选项中,EAR 是完全独立的。通过使用不同的全局 Java 命名目录接口(Java Naming Directory Interface,JNDI)名称多次(每个 EAR 一次)部署同一个 EJB 类来处理共享 EJB 组件 ― 使本地 JNDI 名在 web.xml 文件和 ejb-jar.xml 文件中保持一致,但当部署 EAR 文件时,更改本地和全局名称之间的运行时绑定,如图 3 所示:

  图 3. 独立部署

  要了解该部署选项的工作原理,让我们研究一下它在 IBM WebSphere Application Server V4.0 中的实现方式。Application Server 有一个在域级别上管理的“全局”JNDI 名称空间。Application Server V4.0 中的 JNDI 命名服务在 Admin 服务器上运行,所以虽然每个节点有一个 JNDI 服务,但它们都共享保存在公共管理数据库中的同一个全局名称空间。尽管实现不同,但 Application Server 5.0 也有一个全局和本地名称空间,所以在新版本中原理是一样的。然而,这有两个部分:本地引用(例如,java:comp/env 项)是特定应用程序所特有的,因为它们是在 web.xml 和 ejb-jar.xml 文件中指定的。这表示我们的代码可以引用这些名称,并确保如果这些名称下面的配置更改,则代码也不必更改。然后,第二部分是这些本地名被绑定(在部署时)到共享的全局 JNDI 名称空间中的名称。所以,我们可能会有表 1 中所示的情况:

  表 1. 绑定到全局 JNDI 名称空间中名称的本地名称

Web 应用程序本地 JNDI 引用全局 JNDI 引用
Benefitsjava:comp/env/ejb/EmployeeManagementHomebenefits/com/ibm/ejbs/ EmployeeManagementHome
Timesheetjava:comp/env/ejb/EmployeeManagementHometimesheet/com/ibm/ejbs/ EmployeeManagementHome(不同的全局引用)

  该方法解决了在尝试分割跨 EAR 的应用程序时发生的许多问题 ― 最显著的是:当两个应用程序各自都依赖于稍有不同的共享 EJB 组件版本时发生的“API 版本漂移”问题也解决了。我们常常听到有关该解决方案的争论是:它“产生了太多的 EJB”而且浪费内存。这个说法完全错误。我们可以调优 EJB 服务器和容器的高速缓存大小,使这种方案的内存管理比共享方案中的更有效。另外,许多不熟悉 EJB 技术(尤其是实体 beans)的人认为:该解决方案将不起作用,因为操作实体 beans 的方法有点“不可思议”― 不知何故,对于任何数据块只有一个实体 bean 对象,以多种类似于此的方法部署 bean 将破坏数据管理。这也是个错误观点,因为当在类似于本解决方案中的群集环境中有多个应用程序克隆时,也会有行锁定和隔离级别问题。正如在 EJB 2.0 规范的 10.5.9 章节中所定义的那样,实体 beans 中在数据库级别管理并发性,除非我们正在使用 EJB 提交选项 A,这通常在群集环境中不起作用。在任何情况下,选择该方法将丝毫不会影响数据的一致性或访问速度。

  然而,该解决方案仍可能会引起另一个问题,而该问题会产生许多新问题。在解决“API 版本漂移”问题时,我们已经使自己暴露在一个更微妙的“数据/外部漂移”问题面前。现在,一些应用程序使用的 EJB 代码版本比其它应用程序旧。在实现较新版本代码的过程中,很有可能更改数据库模式,而这样可能会破坏旧的代码。除非我们保存两个产品数据副本(数据管理恶梦),否则我们必须十分仔细地实现数据库模式更改。

  另外,在更改业务逻辑时,甚至会产生一个更微妙的问题。通常,对外部接口的更改表示 API 工作方式的语义更改。然而,有时更改语义时没有更改外部 API ― 例如,可能会修正关键错误,或者可能重构复杂的业务逻辑块,以获得更佳性能。这种情况的主要问题是:如果我们选择独立部署,则 只能通过更新所有其它 EAR 文件来处理这种类型的更改。因而,当发生这种情况时,共享服务部署实际上更可取。

  权衡选项

  按照我们的开发小故事,似乎独立部署对于每种应用程序开发方案都是正确选择。但事实并不是这样。在我们的方案中,简化了几个考虑事项,而正是由于这些考虑事项,使得在许多不同的情况下,使用共享服务部署比使用独立部署更合适。虽然您应该仔细地考虑在许多部署方案中使用独立部署,但它并不适合于每种情况。特别是,如果满足下列任一条件时,应该选择共享服务部署:

  如果逻辑应用程序十分复杂(多个 JAR 文件),那么可能很难将其所有逻辑部件都包括在每个单独的应用程序中。然而,您无论如何都应该留意应用程序被打包在多个 JAR 文件中这一事实。如果您认为您自己必须包括几个 JAR 文件来表示单个逻辑应用程序,那么把逻辑应用程序制作成一个较大的 JAR 文件可能会更好。在 J2EE 中,尤其对于 EJB-JAR 文件的颗粒度下限有一些经验法则(如应该将多少个 EJB 组件放在单个 EJB-JAR 文件中)。例如,对于任何 IBM EJB 扩展(象实体 bean 继承)或任何 EJB 2.0 扩展(关系等),只有同一个 EJB-JAR 文件中的 bean 可以相互引用。然而,上限不太清楚 ― 单个 EJB-JAR 中有多少个 bean 算太多不太明确。颗粒度级别通常根据开发优先级设置而不是根据部署考虑事项设置(例如,对于每个开发人员或每个开发小组可能有不同的 EJB-JAR)。如果应用程序有特定的部署约束,那么可能会证明独立部署的实现很难或花费很大。例如:

  应用程序可能将 JMS 与 WebSphere MQ 一起使用或者将 JCA 与 CICS Transaction Gateway(CTG)一起使用,而您不想在每个应用程序服务器中重复那些产品的安装。例如,您可能不想安装几个 WebSphere MQ 服务器副本,以使生产环境的整体软件成本最小化。

  应用程序可能有复杂的管理或状态信息。或许,它创建多个线程,这些线程以复杂的方式运行以处理工作。将这种应用程序与其它应用程序集成会很困难。

  应用程序可能有严格的安全性约束并可能包含需要谨慎保护的信息(如密码)。这种信息应该尽可能地集中存放。因而,将应用程序内容复制到另一个应用程序是不明智的做法。

  也可能您的应用程序数据需要谨慎地保护,让其它应用程序直接访问这些数据是不合适的。

  如果应用程序的基本外部接口定义良好且稳定,但该实现的内部很可能更改,那么实现独立部署时要小心。内部更改的示例包括更改数据库模式、更改业务逻辑或者甚至更改实现的基础结构(新的数据库、新的第三方产品等)。在这种环境中,最好分隔系统层,这样可以以基本方法更改第二个应用程序,而不影响第一个应用程序。只要您预先花时间设计稳定且良好设计的会话 bean 接口,共享服务部署在这种情况下就会表现出其优势。

  如果部署环境涉及大量分布在不同地点的数据中心,则独立部署会没有效率。例如,如果应用程序 1 的数据在数据中心 1 中,应用程序 2 的数据在数据中心 2 中,则没有好的位置来放置组合的应用程序 1 和 2。因此,最好将应用程序 2 放置在数据中心 2 中并配置它,使之对应用程序 1 进行远程 EJB 组件调用。

  通常,应用程序越复杂,实现独立部署就越困难。复杂的应用程序通常有复杂的管理、安装和初始化过程(许多配置文件和 Application Server 配置约束等)。在这种情况下,将它们与其它应用程序组合在一起会给客户机应用程序带来很大负担。只向客户机应用程序提供适当的客户机 EJB-JAR 会简单些。这样,客户机只需要处理客户机应用程序的客户机接口,而不需要处理其余部分。当然,对于简单的应用程序来说,组合它们远比处理分布式环境的复杂性要容易得多。即使在独立部署可以节省时间和工作的情况下,许多组织也不考虑它。

  如果必须实现共享服务部署,在现有的 J2EE 工具不直接支持该选项的情况下如何使它工作呢?事实上,您必须为该工具找个折中方案。一个选项是开发您自己的定制工具功能,使之可以“剥离”标准 EJB-JAR 文件并打包新的客户机 JAR 文件。至少,从 EJB-JAR 文件中除去 ejb-jar.xml 文件会将它变成客户机 JAR 文件,因为如果缺少 ejb-jar.xml 文件中保存的信息,开发工具将不会试图在 JAR 内部部署 EJB 组件。然而,在大多数情况下,您会希望“剥离”的 JAR 文件的额外部分,以使它尽可能的紧凑(例如,包括 EJB 实现类和生成的框架和持久性类)。

  最后,我们需要考虑的另一个共享服务部署变化就是是否有可能将共享组件(在我们的示例中是 EmployeeManagement bean)完全分割成其自己的 EAR 文件,并与任何其它特定于应用程序的代码完全分离。对于最适合选用共享服务部署的应用程序来说,这或许是非常好的的长期解决方案。然而,这带来了其自身的一些问题:一旦组件与应用程序代码分离,现在谁拥有它们并负责维护它们呢?如果已经建立了专门的重用组来维护共享分布式组件,那么这可以工作得很好,但如果没有建立,那么当共享组件出错时,这种选择会导致相互推脱,都说“这不是我的事情”。

  从中得到哪些知识

  那么,您如何继续下去呢?下面是所涉及问题的一些总结:

  EAR 是一个部署合同。每个 EAR 都应该表示一个完整的应用程序,并且不应该依赖 EAR 文件外部的代码,除了应用程序服务器本身提供的以外。

  不同的逻辑应用程序应该放在单独的 EAR 文件中。当在 J2EE 组件之间存在依赖性时,必须仔细设计并考虑可用性。从理论上讲,应该设计松散耦合的应用程序,每个应用程序在任何时候都不依赖于另一个可用的应用程序。这最好通过使用象采用 JMS 的异步通信之类的技术来完成。然而,当业务需要时,也可采用同步通信。可是,请紧记,该需求对相关应用程序带来了很重的负担。如果许多应用程序非常依赖于彼此的完全可用性,那么系统不太可能会长期工作下去。

  每个 EAR 版本都包含了由该逻辑应用程序版本组成的 J2EE 组件集合。在本文所讨论的示例中,Benefits 应用程序应该由 Benefits WAR 文件和 Timesheet EJB-JAR 文件(以及任何其它需要的 J2EE 组件一起)组成。

  必须在源代码控制管理(Source Control Management,SCM)系统中单独对每个 J2EE 组件(WAR 或 EJB-JAR)进行版本控制和维护。这允许您通过共同配置适当的 WAR 和 EJB-JAR 文件的正确版本来构建 EAR 文件。如果 Benefits 应用程序 V2.0 需要 Timesheet JAR 文件 V1.0,那么当 Timesheet 应用程序 V2.0 需要 TimeSheet JAR 文件 V1.1 时,您可以很快地识别并解决这一问题。

  事实上,对这最后一点值得进行少许澄清和提供一些额外提示。我们鼓励的一种实践是:J2EE 组件(EAR、WAR 和 JAR)包含额外的、人类可读的元数据,该元数据指出组成它的各种子组件的版本。例如,一个 EJB-JAR 文件可能包含特殊的 XML 文件(不是由应用程序服务器使用的,而是由人读取并可能是由 SCM 工具或脚本生成),该文件列出了它所包含的不同 EJB 组件的版本号。如果 EJB-JAR 或 WAR 文件依赖于其它 J2EE 组件的其它版本,那么它还应该将该信息包括在它的 XML 元数据文件中。同样,EAR 文件应该包含它所包含的 WAR 和 EJB-JAR 文件的版本列表。这样,在构建 EAR 时,就可以自动捕捉版本不匹配问题。如果检测到不匹配,那么在部署之前,构建会异常终止。

  结束语

  打包和部署相关的 J2EE 应用程序是一个复杂问题。对于每个环境或应用程序都没有单一的答案。在本文中,我们提供了有关部署 EAR 文件的一些简单建议,从而避免相关应用程序之间的版本漂移、不兼容的硬件需求和不兼容的可用性需求问题。我们很快排除了一种我们时常见到的方法,然后介绍了两种更有用的方法(共享服务部署和独立部署)。我们还演示了在为特殊问题选择正确的部署选项时您必须权衡的不同因素。

  虽然我们建议您在最简单的解决方案(独立部署)适用时就采用它,但我们还提供了一些必需的工具,以了解它何时不适用而需要一个更复杂的方法。这应该有助于您确定如何非常好的配置您自己的应用程序服务器以及如何避免不正确部署可能引起的一些更棘手的问题。

0
相关文章