级别: 初级 Sing Li (westmakaha@yahoo.com), 作者, Wrox Press
2005 年 5 月 23 日 自早先开发人员共享 GUI 库以来,基于 Java 的开源开发已经走了很长一段路。Geronimo 是一个大型项目,目的是基于现有的开源组件创建一个认证的 J2EE 1.4 服务器。让 Sing Li 带您走过 Geronimo 迷宫吧。在这篇由两部分组成的系列文章的第 1 部分中,您将了解 Geronimo 的优雅设计和大胆架构。
在 2003 年 8 月,Apache Software Foundation(负责流行的 Apache HTTP 服务器开发的团队)宣布计划创建一个开源的、认证的 J2EE 服务器 —— 于是诞生了 Geronimo。作为 J2EE 兼容服务器,Geronimo 是一个很大的项目,包括了不同的功能集。在这篇由两部分组成的系列文章的第 1 部分中,我将从使用者的角度介绍 Geronimo,让您对该项目真正涉及的范围有一个正确的认识。然后我将解释一些在学习 Geronimo 文档或者分析这个项目的源代码时肯定会遇到的一些术语。最后,从系统设计的角度对 Geronimo 进行概述 —— 其中有几个重要概念。
阅读完本文之后,作为该服务器的使用者,您可以自已对 Geronimo 作进一步的探索 —— 甚至可以参与开发这个开源项目。本系列的第 2 部分将提供一些使用该服务器的实际体验,将更详细地探讨应用程序的配置、开发和管理。
我衷心感谢 Geronimo 团队的 Geir Magnusson、Jr.、Jeremy Boynes、David Jencks 和 Alan D. Cabrera 对本文初稿提供的宝贵意见。
Geronimo: J2EE 1.4 兼容服务器
作为 J2EE 服务器,Geronimo 可以部署和运行 Web 应用程序和企业应用程序。可以使用 Java ServerPage(JSP)、servlet、filter 和 Enterprise JavaBean(EJB)来构建应用程序。这个应用程序可以通过 Java Data Access API(JDBC)连接器访问外部 RDBMS、通过 Java Naming and Directory Interface(JNDI)访问目录服务、通过 Java Message Service(JMS)访问事务性消息队列、通过 JavaMail 访问电子邮件,等等。
对于 Geronimo 项目而言,进行 J2EE 认证既有好处也有坏处(请参阅 认证的代价)。为了具备认证资格,Geronimo 必须支持 J2EE 规范中规定的所有强制功能(请参阅 参考资料)。该规范还引用了其他一组规范,而这些规范都有自已的强制条款。图 1 给出了具备认证资格 Geronimo 所必须实现的一些内容。
 |
J2EE 认证的负担
J2EE 1.4 认证是一个复杂的、特别严格的过程,需要项目团队中许多成员的全力以赴。即使全职的、商业的专业开发团队,也会对这个过程感到畏惧。一方面,它占用了本来可以用来编写代码的宝贵时间,另一方面,由于能够确保高度的兼容性和功能,所以一些已认证产品的吸引力要大得多,特别是对于企业开发人员来说。
|
|
图 1. 作为 J2EE 1.4 兼容服务器的 Geronimo
在图 1 中,框中的粗体字是特定 API 的名称,而斜体字指出了 Geronimo 目前是如何实现它们的。您可能认出了一些斜体的项目名。
开源项目的一个混合体
Geronimo 的服务器基础设施 —— 由核心、工具和库组成 —— 是由有经验的开发人员从头开始设计和编写的。但是使用的许多构成组件是从 Apache 自己的项目社区中选择出来的已有项目,或者是从其他具有与 Apache 许可兼容的许可的开源项目中选择出来的已有项目(请参阅 参考资料 和 为什么推出另一台 J2EE 服务器?)。
表 1 列出了 Geronimo 目前包括的其他许多开源项目。这种分开-整合策略使得经过测试和已证实的开源资源可用于不同的目的,并显著减轻了 Geronimo 的整体开发负担。
表 1.
Geronimo 中集成的开源项目
|
J2EE 认证需要的规范
|
覆盖范围
|
Geronimo 使用的项目或者代码
|
Servlets 2.4
JavaServer Pages(JSP)2.0 | Web 层容器,支持 JSP 和 servlet | Jetty 和 Tomcat(请参阅 参考资料)
| |
Enterprise Java Bean(EJB)2.1 | EJB 容器 | OpenEJB(请参阅 参考资料)
| |
Java Message Service(JMS)1.1 | Messaging 服务 | ActiveMQ(请参阅 参考资料)
| |
Java Naming and Directory Interface(JNDI)1.2.1
| Directory 服务/naming API | 自定义代码实现
| |
Java Transaction API(JTA)1.0 | 事务 | 自定义管理器,带用于事务日志的 High-speed ObjectWeb Logger(HOWL),支持 XA 支持,发展为 Java Open Transaction Manager(JOTM)(请参阅 参考资料)
| |
JavaMail 1.3 | 邮件 | 自行编写
| |
JavaBeans Activation Framework(JAF)1.0 | 激活 —— 处理 html/text/gif/jpg MIME 类型,主要用于 JavaMail 附件 | 自行编写
| |
JSR 77 -- J2EE Management 1.0 | 可管理性 | 用 MX4J 自行编写(请参阅 参考资料)
| |
JSR 88 -- J2EE Deployment 1.1 | 部署和配置 —— 可跨不同厂商服务器部署 | 自定义代码实现 | |
Java Management Extensions(JMX)1.2 | 可管理性 | MX4J(请参阅 参考资料)
| |
Java Data Access API(JDBC)3.0, 2.1 | 数据库 | 代码来自 TranQL(请参阅 参考资料)
| |
Java API for XML Processing(JAXP)1.2 | SAX, DOM APIs; 第三方 SAX、DOM、XSLT 引擎的可插入性 | JDK 支持(如果有的话)和 Apache Xerces
| |
J2EE Connector Architecture(J2CA)1.5 | 连接器 | 自定义编码。包括 JMS 资源和 JDBC 工具
| |
JSR 109 -- Implementing Enterprise Web Services 1.1 | Web 服务 | Apache Axis(请参阅 参考资料)
| |
Java API for XML-based RPC(JAX-RPC) | Web 服务 | Apache Axis
| |
SOAP with Attachments API for Java(SAAJ)1.2 | Web 服务 | Apache Axis
| |
Java API for XML Registries(JAXR)1.0 | Web 服务 | Apache Scout
(请参阅 参考资料) | |
JSR 115: Java Authorization Contract for Containers(JACC) | 安全性 —— 授权和身份认证 | 使用 JDK 的 JAAS 支持自行开发
| |
内部数据库 | 数据库 | Derby(请参阅 参考资料)
| |
持久存储机制 | 数据库 | 在 TranQL 上实现面向 CMP 和 Beans/POJO 的方案的统一基础(请参阅 参考资料)
| |
互操作性 | TCP/IP, HTTP1.1, SSL3.0, TLS 1.0, SOAP 1.1, WS-I Basic Profile 1.0
CORBA - IIOP, RMI-IIOP, EJB Interop, CORBA Interop Naming Service, JRMP | JDK 支持(就是 ORB 和 JRMP)、来自其他包的支持和自定义代码
|
 |
为什么推出另一个 J2EE 服务器?
因为已经有了其他开源 J2EE 服务器,甚至有一个 J2EE 服务器已经通过 J2EE 1.4 认证(请参阅 参考资料)。不过,Geronimo 的独特之处在于它对 Apache 许可的统一支持(请参阅 参考资料)。Apache 许可通常被看成是这类许可中最自由的一个,其他许可大部分是 GNU General Public License(GPL),只有少数是 Lesser General Public License(LGPL)。这些许可的法律细节在过去十年内一直有争议。实际上,Apache 许可是惟一允许将其代码自由使用于任何目的许可,不管该目的是不是商业性的,只需要注明来源即可,无需为所获得的好处承担任何义务。
|
|
统一复杂系统
图 1 中所描绘系统的复杂性足以让大多数系统架构师头痛。但是必须将这些系统集成到一起,构成 Geronimo 的开源项目,因此,统一集成模型就浮现了出来。
我们原先打算创建一个一般性的服务容器,用它部署和运行任意的服务。例如,OpenEJB 可以作为服务在这个容器上运行,以承载所有 EJB;而 Jetty 或者 Tomcat 可以作为同一容器中的另一项服务,来承载所有 servlet 和 JSP。这种模型不仅使现有开放源服务的集成变得很直观,而且还提高了将来承载任何兼容服务的能力,这些兼容服务中包括非 J2EE 服务,如 Spring 应用程序框架(请参阅 参考资料)。
容器的设计必须保持一般性,同时支持插入 J2EE 和非 J2EE 服务。这是一项非常困难的设计任务。容器应当处理所有没人愿意做的繁琐和重复性的工作,但是它并非无所不能,因而它采取了插入服务的设计形式。Geronimo 的设计者利用 J2EE 1.4 规范确定了所有服务需要的三个功能领域:
这个列中包括 Geronimo 服务容器的三种主要功能。稍后您将会看到 Geronimo 是如何满足这些要求的。
崇高目标:可伸缩性、管理性和配置管理
通过借鉴远程通信业的一些经验,Geronimo 的设计者试图解决大规模可管理性和配置管理的问题。术语操作、管理、维护和预测(OAM & P)已经成为远程通信系统工程师生活的一部分,但对于 Java 系统设计人员而言,这些还是一些比较新的概念。对于要同时运行数十万个服务、并且每分钟都有故障-恢复发生的系统,对操作、管理和维护系统的看法显然会有很大的不同。例如,人工修改每个实例的配置文件是完全不可行的。
在最终的版本中,可以向 Geronimo 实例群集提交以下工作订单:
假设在高峰时,Pet Store Web 应用程序每小时有 50000 位客户的负荷,那么可以限制 Service Level Agreement 在 99% 的时间内是可用的,而每个订单的处理时间不超过 5 秒钟,这样的限制一直要持续到 6 月 15 日。6 月 15 日,负荷高峰降低到了每小时 10000 位客户,因此可以限制 SLA 在 80% 的时间内可用,并且每个订单的处理时间不超过 10 秒,该限制将一直持续到 8 月 31 日。9 月 1 日将删除这个 Web 应用程序。
收到了工作订单后,网络管理系统就会在整个群集中准备并规划对所有配置和部署所做的更改,然后监视进程,并随时间的推移相应地调整系统,以实现您的目标。
这一场景与今天的现实仍然有相当大的差距(至少对非远程通信系统如此),但是从第一天设计 Geronimo 架构开始,设计者们就考虑到了这一点。
让事情变得可管理
设计实现一个与 Geronimo 远景一致的、可用的容器的第一个基本步骤是确保容器是可管理的。Geronimo 团队实现这个目标的方法也在与时俱进。
JMX 转向
实现可管理性的普通方法是使 Java Management Extensions(JMX)成为容器的核心。如果容器中所有相关对象实例都已经过正确测量,并且都是可管理的,那么正在运行的系统就可以用外部工具来管理、监视和控制,并且可以实现可伸缩性。图 2 中展示的是 Geronimo 核心设计的第一步,我们的设计就是从这种思路中发展出来的。
图 2. 用 JMX 核心设计的 Geronimo 内核
在图 2 中,JMX 在容器的核心深处 —— 在内核中,容器中每一个相关对象都是一个 MBean。因为需要使用 MBean 来实现完全测量和可管理性。所以,Geronimo 团队选择了 MX4J(请参阅 参考资料)。
不过,实际上 Geronimo 团队的人也认识到使用 MBean 超出了 MX4J 原来的设计。Geronimo 不再只是将 MX4J 当成一个 JMX,而是把它当作一个通用的、大型的系统,用于对象定位、对象间通信、方法截获,等等。当团队沿着这条路继续前进时,容器变得越来越复杂,性能也开始下降。
团队的另一个发现是:很难在一个以管理为中心的核心系统中注入现有的服务。在图 2 中,JMX 核心上面的框表示服务,垂直的点线 "分支" 表示处理可管理性问题的代码。涉及可管理性的代码以及以 JMX 为中心的服务被设计成一条线,而服务逻辑代码与这条线相交(或者横切)。这创建了难于编写和维护的服务代码。现在是更改计划的时候了。
构建一个更好的内核 —— IoC 样式的内核
图 3 中显示了 Geronimo 修改后的内核设计。
图 3. Geronimo 的当前内核心架构(IoC 内核)
 |
关于 IoC
控制反转,也称为 依赖注入,是一种受 IoC 容器和框架支持的模式,使用它可以实现关注点分离。容器中的组件可以隔离依赖性,并且可以在执行/部署时将这些依赖性注入。这使容器可以对实现进行选择,从而将控制权从组件转移到容器上。有关的更多信息,请参阅 参考资料。
|
|
如图 3 所示,JMX 实现不再是内核的核心。相反,内核架构是以控制反转(Inversion of Control)为基础(IoC,请参阅 关于 IoC)。JMX 仍然是内核中很重要的一部分,因为可管理性仍然是最重要的,但是它降级为可在部署时添加到服务器中的许多依赖性之一。这对创建新的服务有很大的帮助,因为代码可以(也应该)以服务逻辑为中心。要获得 JMX 可管理性,只需要根据 IoC 依赖性添加一些编码规范即可。这种设计也使得从 Geronimo 中删除已有的服务变得更简单,可以围绕已有的服务创建一个瘦包装器,将它用于 IoC 内核。
注意,服务不再由 JMX MBean 表示,而是由特定于 Geronimo 的 GBean 表示,在下一节中,将介绍它的更多内容。
这种设计可以支持完全没有 JMX 的、未管理的操作,在这些操作中,组件只能通过内核进行交互。它提供了一个轻量级解决方案,在该方案中,Geronimo 被用作 IDE、命令行工具或者其他客户应用程序的一个嵌入式组件。
Geronimo 的术语
到目前为止我所使用的术语都是一般性的(除了 GBean)。在进入下一步之前,我还要介绍一些特定于 Geronimo 的术语,以及它们的定义。
模块
与可部署单元的 J2EE 术语一致,每一个可部署的软件单元通常都被称为模块(请参阅 模块和配置)。一个模块可以包含许多 Java 类和 GBean,通常与一个或者多个 Geronimo 部署计划相关联。(我将在下一小节中描述 GBean,并解释 Geronimo 计划。)例如,模块可以是 Web 归档文件(WAR)中的 Web 应用程序,也可以是企业文档(EAR)中的一个企业应用程序。模块是一个用户概念。在内部,Geronimo 内核看不到模块,只对模块的配置进行处理(在后面介绍它们)。
Geronimo 部署计划
Geronimo 部署计划 是特定于 Geronimo 的元数据,是成功部署模块(配置)所必需的。物理上,它是一个 XML 文件。它可以位于一个模块的文档中,或者存储在外面某个地方(可以用部署/管理工具管理和编辑)。Geronimo 部署计划 XML 文件可以有不同的名称,该名称取决于模块类型及其位置。例如,嵌入到 JAR 中的 EJB JAR 模块的 Geronimo 部署计划被称为 openejb-jar.xml,而嵌入文档中的企业应用程序 EAR 模块的 Geronimo 部署计划被称为 geronimo-application.xml。当部署计划是在文档外部维护时,它们可以具有任何名称。Geronimo 拥有一些用于简单模块的默认部署计划,比如 WAR、EAR 和 JAR。
GBean
一个模块就是一个可部署单元,而一个 GBean 就是 Geronimo 中一个可管理单元。单个可部署单元可以包含零个或者更多可管理单元。因此,Geronimo 中的一个可部署模块可以部署许多 GBean。并不是模块/配置中的所有类都要成为 GBean。只有那些接收生命周期回调的实体或者要公开 JMX 可管理属性和操作的实体才必须成为 GBean。一个 GBean 可以拥有多个运行的实例。在启动 GBean 时,Geronimo 为每个实例指定一个惟一名称。在内部,Geronimo 只看到 GBean 及其配置,看不到模块。模块是用户概念,GBean 是系统概念。
 |
模块和配置
模块 其实就是一个 J2EE 术语,而不是特定于 Geronimo 的术语。在部署时,一个或者多个模块打包到一个配置中。Geronimo 内核只了解配置。一个配置就是您想要运行的某些内容(例如,一个应用应用程序、一组服务,或者只是一组 JAR 文件)。之所以需要模块,是因为不希望总是管理单独的组件。系统通常有一些紧密耦合的组件群集,而那些组件是松散耦合在一起的。通常对每一个紧密耦合的组有一个配置。
|
|
配置
配置 是一个或者多个模块的组合部署。在内部,Geronimo 将配置视为一个或者多个 GBEan 的打包部署。Geronimo 内核要了解并管理配置。可以将配置想像为要运行的某些内容。在 J2EE 中,它可以是企业应用程序,在 J2EE 以外,它可以是您所创建的一组自定义服务。
配置背后的一个重要思想是将配置序列化,然后在任何运行 Geronimo 的其他系统上反序列化,并在其他 Geronimo 实例上运行同样的配置。移植配置的能力对于可伸缩性非常重要。例如,想像一下需要启动 500 个 Geronimo 实例的 Pet Store 应用程序。有了统一的 Pet Store 配置,任务就会非常简单,只需要使用序列化的配置启动实例即可。
配置通常还存在外部依赖性。由容器负责管理这些依赖性,并确保它们在请求时可用。
运行的 Geronimo 系统由许多运行的配置组成。配置可以具有层次关系。每一个配置都有一个惟一的、特定于用户的配置 ID。一个配置可以指定其他配置作为其父配置。而子配置可以继承其父配置的类装载器,从而访问所有父配置的类。这有时非常有用,比如,当需要在两个分别部署的配置中共享库的时候。
基本 Geronimo 概念
在这一节中,将介绍的一些重要 Geronimo 概念,帮助您分析 Geronimo 或者它的源代码。
GBean 生命周期
GBean 远远超出了 JMX MBean。GBean 为可管理的服务提供了良好外观。GBean 的好处之一是容器可以管理其服务的生命周期。如果 GBean 实现了可选的 GBeanLifecycle 接口,那么当状态变化时,容器将调用 GBean,如清单 1 所示:
清单 1. GBean 的可选 GBeanLifecycle 接口
public interface GBeanLifecycle {
void doStart() throws Exception;
void doStop() throws Exception;
void doFail();
}
|
图 4 显示了 GBean 经历的状态变化。
图 4. GBean 生命周期和 JSR 77 管理状态
在图 4 中可以看到,包含 GBean 的模块可以部署或者分布到某一个目标上。这个目标可以是一台计算机,或者是计算机群集。分布到一个目标时,要对 GBean 配置进行验证和存储,但是不在目标上进行。如果部署到目标上,那么 GBean 就会启动。
服务器停止运行时,GBean 的持性状态将持久存储在配置存储器中。这意味着下一次服务器重新启动时,将启动同一个 GBean 配置。如果取消部署 GBean,那么相关联的配置就会从目标中删除。所有运行实例都将停止,如果不重新部署 GBrean ,将无法重新启动它。
 |
两个重要的规范:JSRs 77 和 78
JSR 77 是 J2EE 管理规范,描述了通过不同的管理工具、使用不同的协议使应用服务器可管理的管理模型。JSR 88 是 J2EE Application Deployment 规范,描述了用 DDBean 表示部署描述符片段的概念。从 表 1 中可知, 这两个规范都是在 J2EE 服务器中使用的规范,必须实现它们来满足 J2EE 认证需求。
|
|
在 starting(0)和 stopped(3)状态之间是触发对 GBeanLifecycle 接口的回调的状态转换。它们也对应于 JSR 77 可管理状态(请参阅 两个重要的规范:JSRs 77 和 78)。
配置管理
配置是以有序形式持久存储到 配置存储器 中的。Geronimo 支持配置存储器的插件提供者。例如,可以不想将配置存储到本地磁盘中,可以通过一个 JNDI 提供者将它们存储到外部目录服务中。这有可以使在地理上分离的 Geronimo 实例群集共享配置。
因为 Geronimo 支持 JSR 88 要求的部署工具互操作性,可以用 JSR-88 兼容的第三方工具编辑配置存储器中的值,这些值是所部署模块的当前参数,可以是在基于 GUI 的 IDE 中对它们进行部署,也可以通过网络管理系统对它们进行部署。
依赖性管理
依赖性管理 是 Geronimo 中的关键部分。启动特定的配置经常需要启动其依赖关系。例如,通过 JDBC 访问 JMS 和 RDBMS 的 Web 应用程序需要确保已经启动 ActiveMQ,并且 JDBC 连接器已经启动并可用。依赖性管理器扫描所有模块的部署描述符和部署计划,以确定它们之间的依赖性。由依赖性管理器保证在启动模块之前,模块的依赖关系可以得到满足。在 Geronimo 部署计划中,要明确指定模块的依赖性。
编写 GBean: 引用和交互
乍看之下,在成功地部署和运行 GBean 后,容器的任务就几乎已经完成了。但是事实远非如此。容器需要满足的另一个主要功能是 "编写" GBean 之间的引用,使它们可以交互。
配置启动后,所有获得另一个 GBean 的引用的 GBean 都有一个容器生成的代理。这个代理与所引用的 GBean 具有完全相同的接口。这使两个 GBean 可以解耦合、并使容器可以管理所引用的 GBean 的生命周期。
到其他 GBean 的可解析引用是通过动态生成的调用器代理访问的。这使得 GBean 可以不使用内核而彼此调用。图 5 展示了这种行为。
图 5. 编写 GBean
在图 5 中,GBean 1 调用 GBean 2 上的一个方法。GBean 2 是可解析的引用,而 Geronimo 动态生成了一个调用器代理。调用器使得两个 GBean 可以直接交互。Geronimo 目前使用一种字节码生成工具 cglib(请参阅 参考资料)来动态生成调用器代码。
结束语
Geronimo 有可能成为服务器端产品开发的最有吸引力的容器之一。它丰富的功能集、无责任要求的 Apache 许可和 "随时可部署" 的、认证的 J2EE 1.4 容器等一些好处对于许多开发人员来说确实很有吸引力。读了本文后,您应该了解 Geronimo 服务器是什么、以及为什么它是开源软件开发的一个重要里程碑。我们还探讨了 Geronimo 的架构,并熟悉了一些关键术语和概念。本系列的第 2 部分将引导您完成一些在实际的服务器上运用的实用例子。
参考资料
关于作者
对本文的评价
|