UML软件工程组织

利用本地和远程 EJB 接口来实现部署灵活性和优化的性能
作者:Sheldon Wosnick、Viktor Stepanchuk 选择自 IBM
本文展示了样本 J2EE 应用程序的设计方法、实现和测试结果,提供了 Enterprise Java Bean 的本地和远程接口使用的基本比较。

引言
随着 EJB 2.0 规范的出现,Enterprise Java Bean 可以有本地接口、远程接口,或同时有两种接口,这给 J2EE 开发人员和架构师提供了非常大的灵活性。实现两种接口给予 Bean 客户端和 Bean 本身在开发场景方面的自由。取决于客户端与 EJB 的相对位置,可以设置访问 Bean 的逻辑的最佳方式。本地接口提供对 EJB 的优化访问(如果客户端和 Bean 位于同一 Java 虚拟机(Java Virtual Machine,JVM)中),而远程接口可以用于分布式体系结构。在单个应用程序中组合这两种类型的接口来支持两种访问 Bean 的方式有助于减少设计约束。

通过使某些事情具体化来权衡设计决策的利弊是非常有意义的,这样的事情可以是测试结果、案例研究,最好是方法和工具而不要仅仅是请求和响应。本文力图提供更多的关于使用 EJB 的本地和远程接口的信息(基于样本 J2EE 应用程序)。通过使用 WebSphere Studio Application Developer 的开发和测试环境,我们将实现两种 J2EE 应用程序体系结构的拓扑,运行应用程序,并且借助于简单的 Java HTTP 客户端对应用程序进行测试来获取与性能有关的反应和数据以供考虑。

J2EE 应用程序的体系结构选择
一般来说,最简单的拓扑是 J2EE 应用程序中的每个组成部分都在同一 WebSphere Application Server、同一 JVM、同一节点或机器中。在这样的情况下,WebSphere Application Server 提供 Servlet 容器和 EJB 容器(图 1)。这种遍及全文的拓扑(我们将省略防火墙、负载平衡器和转换器等等)称为“All-in-One”拓扑。

图 1. 简单的 J2EE 应用程序拓扑:All-in-One
简单的 J2EE 应用程序拓扑:All-in-One

这是 WebSphere Studio Application Developer Version 5.1(以下称为 Application Developer)中的 WebSphere Test Environment(WTE)的缺省拓扑。然而,这样的拓扑可以作为站点体系结构的一部分成功地用于生产环境。随着 EJB 2.0 规范的出现,可以使用 J2EE 应用程序的任何 Java 组件中本地接口来与同一 EJB 容器中的 EJB 进行通信。

有时,需要分离 Servlet 和 EJB 容器,比如出于应用程序安全性考虑。应用程序架构师或开发人员可以选择把 Servlet 容器放在不同的节点上,因而使 J2EE 应用程序拓扑更加复杂(如图 2 所示),我们称这种拓扑为“分离容器(Separate Containers)”拓扑。事实上,WebSphere Application Server 可以用作高效 Servlet 容器和 EJB 容器,但是为了使事情更容易区分,我们在此包含了 Tomcat Application Server 和 Servlet 容器。

图 2. 拓扑:分离容器(Separate Containers)

拓扑:分离容器(Separate Containers)

 

在分离容器(Separate Containers)拓扑中,J2EE 应用程序的 Web 部件与 EJB 通信的惟一方法是通过远程接口(一般是通过 Internet Inter-Orb Protocol(IIOP))。(在本文中,我们不讨论 Web 或 HTTP 服务器的分离,因为它不是我们关注的要点)。

部署体系结构的决策在 J2EE 应用程序的开发过程中发生改变。因此,开发人员可能想利用 EJB 的远程和本地接口的优势来避免损失灵活性。有可能开发一种 J2EE 应用程序,它将适应任何合理的部署拓扑而不用更改一行源代码,惟一需要进行的更改也许就是部署描述符设置,这可以大大节省开发人员的时间。比较本地接口与远程接口的使用性能也是非常有意义的。稍后,我们将创建一个样本 J2EE 应用程序,它将使我们能够通过设置 EJB 的环境参数来方便地从使用本地接口转换到使用远程接口。对于 All-in-One 拓扑,我们将通过控制方式使用本地或远程接口来测试样本应用程序,并且分别称它们为“All-in-One-All-Local”和“All-in-One-All-Remote”。

构件和先决条件
接下来的部分将描述创建一个可灵活部署、多层、分布式 J2EE 应用程序(名为“Dual”)的开发和部署过程,这个应用程序将演示两种接口的使用。结果包括:

  • 三个 J2EE 应用程序(EAR 模块),用于已描述的部署场景。
  • Java 模块,包含一个 Java Bean。
  • 两个 J2EE EJB 模块:
    • 无状态会话 EJB
    • 容器管理的实体 EJB(CMP)
  • J2EE Web 应用程序(WAR 模块),包含:
    • 一个 Servlet
    • 一个 JSP 页面
    • 一个 HTML 页面
  • 数据源(映射到 DB2 数据库)。
  • 两个测试服务器,每个实现:
    • WebSphere Test Environment(WTE)
    • Tomcat Test Environment

这个应用程序采用标准的模型-视图-控制器(Model-View-Controller)体系结构(MVC,“Model 2”),其中,Servlet 用作控制器,而 JSP 用作表示组件。为了使事情更接近于现实生活,还使用了 Data Transfer Object(DTO)或 Value Object(VO)设计模式。Servlet DualServlet 与会话 Bean DualSession 交互,而会话 Bean DualSession 又与实体 Bean DualEntity 交互。实体 Bean 与数据源(包括表 PERSON 在内)交互。VO 是由可序列化 Java 类 Person 表示的。该对象用作会话 Bean 的方法的返回值以及 JSP 页面的 Java Bean。

下载 ZIP 文件包含完整的 EAR 模块和本文所用的代码清单。通过这些现成的解决方案,您可以简单地创建数据库表和服务器项目,然后轻松地运行、调试和测试应用程序。下载的内容还包括一个简单的 Java HTTP 多线程测试客户端来帮助进行性能测试。

在实际场景中,图 1 和 2 展示的每个组件都将驻留在分离的节点(机器)中。出于本文的目的,我们将把每个组件(包括持久性存储在内)放在同一机器中,在单个开发人员的工作台上运行每个组件。由于本配置只是出于教育的目的,它仍将不限定可灵活部署的应用程序的特征。应用程序的每个主要的组件都可以部署到分离的节点中而不用进行代码更改,采取的方式是将相对主机名提供给客户端、Web 和 EJB 组件以及 JDBC 驱动程序。

为了完成本文中的步骤,您必须在工作台上安装如下应用程序:

  • DB2 V8.1(数据库)
  • WebSphere Studio Application Developer Version 5.1(集成开发和测试环境)
  • Tomcat 4.1.29(Servlet 容器);请在此下载

下面几个部分将描述开发 Dual J2EE 应用程序的主要步骤。为了本练习的成功,必须完全遵循命名约定。

重要:本文假定您具有一定的 J2EE 开发经验和 Application Developer 应用知识,因此,我们只提供主要的主题、重要的决策和需要遵循的路径,而不提供过于详细的描述。要了解应用程序的细节和帮助背景信息,请参阅参考资料部分列出的先前的“Hello World”文章。

在 Application Developer 中设置项目

步骤 1. 创建数据模型和持久性存储
用于 Dual 应用程序的数据模型与 “Hello World”J2EE 应用程序中的是一样的。先进入 DB2,再创建名为 dualDB 的数据库,然后在数据库中创建表 PERSON


CREATE TABLE PERSON
(
  ID        INTEGER NOT NULL PRIMARY KEY,
  FIRSTNAME VARCHAR(40),
  LASTNAME  VARCHAR(40)
)

在下载的 ZIP 文件的 Table.ddl 中提供了此脚本。

步骤 2. 创建项目
在这一步中,我们将创建一组项目,其中的每个项目都将用于不同的目的。例如我们将把实体 Bean 和会话 Bean 放在不同的 JAR 中,之所以这样做是因为较大的 J2EE 应用程序常常是由若干 JAR 或项目组成的。

请一个接一个地创建以下项目:

  1. Java 项目:DualJavaModule00
    在此项目中,创建包 dual.value。我们将把 VO 的类 Person 放入其中。
  2. J2EE 1.3 项目:DualEARForAllInOne
    此项目将是 All-in-One 拓扑的占位符。
  3. J2EE 1.3 项目:DualEARForEJBContainer
    此项目只准备用作 EJB 容器。
  4. J2EE 1.3 项目:DualEARForWeb
    此项目只准备用作 Servlet 容器。此外,我们还可以使用 DualEARForAllInOne 来达到相同的目的,因为它们惟一的不同之处在于项目依赖性。然而,最好保持它们分离。
  5. EJB 2.0 项目:DualEJBModule01
    DualEARForEJBContainer 相关联。创建包 dual.ejb(在 ejbModule 文件夹中)。
  6. EJB 2.0 项目:DualEJBModule02
    DualEARForEJBContainer 相关联。同样地,创建包 dual.ejb。使此项目依赖于 DualEJBModule01。(稍后将对依赖性作进一步的介绍。)
  7. Web 项目:DualWebModule03
    在创建此项目时,请选中 Advanced options 复选框,使其与 DualEARForWeb 相关。在此 Web 项目内创建一个包 dual.servlet。不选定所有的 WebProject features,因为此练习不需要它们。

在完成了初始项目结构之后,工作台中的 J2EE 视图应该看起来如图 3 所示。

图 3. 具有项目的工作台
具有项目的工作台

在 Project Navigator 视图中,您可以看到所有已创建的项目。

步骤 3. 创建项目内容

  1. DualJavaModule00 项目中,创建名为 dual.value.Person 的 Java Bean。您可以从下载的内容中复制此 Bean。通过右击项目名并选择 Refresh 来刷新项目。
  2. DualEJBModule01 中,创建具有具有容器管理的持久性(Container Managed Persistence,CMP)的实体 Bean。将其命名为 DualEntity(图 4),具有类型 int 的键字段 id firstNamelastName 作为持久性 String 字段。选中本地和远程客户端视图,如图 5 所示。我们将使用 Remote Home Interface 的名称 dual.ejb.DualEntityRemoteHome 和远程接口的名称 dual.ejb.DualEntityRemote,因而需要更改缺省名。EJB 绑定名将为 ejb/dual/ejb/DualEntityHome

    图 4. DualEntity EJB
    DualEntity EJB

    图 5. CMP 字段和命名约定
    CMP 字段和命名约定

    另一种方法是,将源代码从下载的文件复制到项目文件夹中,刷新项目,然后如上创建实体 Bean,用该代码作为膜板(图 5)。这还将创建 ejb-jar.xml 和绑定描述符。在此之后,您还可以将 ejb-jar.xml 从下载的文件复制到项目中。
  3. DualEJBModule02 中,创建无状态会话 Bean(称为 DualSession)。另外,您还可以复制源代码的内容,刷新项目,然后用该代码作为膜板创建 DualSession 会话 Bean。与上面类似,我们将需要本地和远程接口,Remote Home Interface 的名称为 dual.ejb.DualSessionRemoteHome,远程接口的名称为 dual.ejb.DualSessionRemote。(检查为命名约定提供的源代码。)从下载的文件中复制 ejb-jar.xml。稍后我们将进行代码分析。在分析完成之后,您将得到许多错误消息。现在忽略它们;在下一步确定项目依赖性时将解决这些错误。
  4. DualWebModule03 中,创建名为 DualServlet 的 Servlet。此外,完成这项任务还有一种更快速的方法,就是从下载的文件中复制源代码 DualServlet.java。您也可以将 web.xmlindex.htmlresult.jsp 复制到项目文件夹中。另外,您会得到许多错误消息,但是现在我们将通过确定项目依赖性来解决这些错误。

步骤 4. 确定项目依赖性和 EJB 到 RDB 映射

  1. 通过调整项目特性中的 Java Build Path 来使 DualEJBModule02 项目依赖于 DualJavaModule00DualEJBModule01 项目。在选择 OK 按钮之后,此项目的错误应该消失。
  2. 同样地,使 DualWebModule03 依赖于 DualEJBModule02DualJavaModule00,采取的方式是再次调整项目特性中的 Java Build Path,在 Web Library Projects 设置中压需要进行做同样的调整。此后,您就不应该有错误消息。
  3. 要将 DualEntity Bean 映射到关系数据库(RDB)表 PERSON,可以选择 DualEJBModule01,再右击并选择 Generate... => EJB to RDB Mapping ...,然后使用如下选项:
    • 创建一个新的后端文件夹。
    • 对于 EJB/RDB 映射,使用“Meet In The Middle”选项。
    • 要建立数据库连接,可以选择 DB name, userID, password, DB2 Universal Database 8.1,然后接受剩余选项的所有缺省值。
    • 选择 PERSON 表供导入。
    • 选择 Match By Name and Type 选项。
    • 首先选择 Bean,再选择表,然后右击并选择 Match by Type

      图 6. EJB 到 RDB 映射
      EJB 到 RDB 映射

  4. DualEJBModule01DualEJBModule02 选择 Generate... => Deployment and RMIC Code...

JNDI 绑定和源代码分析

EJB 模块
DualEJBModule01 开始。在 EJB Deployment Descriptor 编辑器面板中,选择 Beans 选项卡,然后选择 DualEntity,并且确保 WebSphere Bindings 部分中的 JNDI 名设置为 “ejb/dual/ejb/DualEntityHome” 。保存所有的更改。

首先,让我们研究 DualEJBModule02 项目中 DualSession EJB 的 ejb-jar.xml 的代码片断:


...
<env-entry>
   <env-entry-name>useLocal</env-entry-name>
   <env-entry-type>java.lang.Boolean</env-entry-type>
   <env-entry-value>true</env-entry-value>
</env-entry>
<env-entry>
   <env-entry-name>doTrace</env-entry-name>
   <env-entry-type>java.lang.Boolean</env-entry-type>
   <env-entry-value>false</env-entry-value>
</env-entry>
<ejb-ref id="EjbRef_02">
   <ejb-ref-name>ejb/DualEntityRemoteHome</ejb-ref-name>
   <ejb-ref-type>Entity</ejb-ref-type>
   <home>dual.ejb.DualEntityRemoteHome</home>
   <remote>dual.ejb.DualEntityRemote</remote>
   <ejb-link>DualEJBModule01.jar#DualEntity</ejb-link>
</ejb-ref>
<ejb-local-ref id="EJBLocalRef_02">
   <ejb-ref-name>ejb/DualEntityLocalHome</ejb-ref-name>
   <ejb-ref-type>Entity</ejb-ref-type>
   <local-home>dual.ejb.DualEntityLocalHome</local-home>
   <local>dual.ejb.DualEntityLocal</local>
   <ejb-link>DualEJBModule01.jar#DualEntity</ejb-link>
</ejb-local-ref>
...

这里,第一个元素(具有名称“useLocal”)是用来控制 DualEntity EJB 的本地接口的使用的。第二个条目(具有名称“doTrace”)准备只用于跟踪。已实现的跟踪功能是相当基本的,但是对于我们的目的来说足够了。

相应的 Java 代码看起来像是下面这样的:


public class DualSessionBean implements javax.ejb.SessionBean {
   private javax.ejb.SessionContext mySessionCtx;
   private boolean doTrace;
   private boolean useLocal;
   private DualEntityRemoteHome entityRemoteHome;
   private DualEntityLocalHome entityLocalHome;

   public void setSessionContext(javax.ejb.SessionContext ctx) {
      mySessionCtx = ctx;
      try {
         Context initCtx = new InitialContext();
         Boolean option = null;
         option = (Boolean)initCtx.lookup("java:comp/env/useLocal");
         useLocal = option.booleanValue();
         option = (Boolean)initCtx.lookup("java:comp/env/doTrace");
         doTrace = option.booleanValue();
         if (doTrace)
            System.out.println("DualSessionBean#setSessionContext: useLocal="
                  +useLocal);
         Object homeObject = null;
         if (useLocal){
            homeObject = initCtx.lookup("java:comp/env/ejb/DualEntityLocalHome");
            entityLocalHome = (DualEntityLocalHome)homeObject;
         } else {
            homeObject = initCtx.lookup("java:comp/env/ejb/DualEntityRemoteHome");
            entityRemoteHome = (DualEntityRemoteHome)
            javax.rmi.PortableRemoteObject.narrow(homeObject,
                  DualEntityRemoteHome.class);
         }
      } catch(NamingException ne){
         // process exception here...
      }
   }
...

在 Java 代码中,EJB 环境变量是作为无状态会话 Bean 对象的生命周期的类变量存储的。

DualEntity 的本地接口和 Remote Home Interface 是通过引用访问的;这是首选的方式,并且遵守 EJB 2.0 规范。相对地,ejb-jar.xml 包含描述这些引用的 ejb-refejb-local-ref 元素。

打开 EJB Deployment Descriptor 编辑器,然后选择 Beans 选项卡。确保 JNDI 名设置为“ejb/dual/ejb/DualSessionHome”。然后转到 References 选项卡,确保对 DualEntity 两个引用指向 JNDI 名“ejb/dual/ejb/DualEntityHome”。必要时可以更改这些值,然后保存所作的更改。

Web 模块
DualWebModule03project 标准的 J2EE Web 部署描述符包含两个重要的代码片段。第一个指定该 Servlet 的初始参数:


...
   <init-param>			
		<param-name>useLocal</param-name>
		<param-value>true</param-value>
   </init-param>
   <init-param>
		<param-name>doTrace</param-name>
		<param-value>true</param-value>
   </init-param>
   <!--
   <init-param>
      <param-name>jndiProviderURL</param-name>
      <param-value>iiop://localhost:2809</param-value>
   </init-param>
   <init-param>
      <param-name>jndiNameOnServer</param-name>
      <param-value>ejb/dual/ejb/DualSessionHome</param-value>
   </init-param>
   <init-param>
      <param-name>initialCtxFactoryClassName</param-name>
      <param-value>com.ibm.ejs.ns.jndi.CNInitialContextFactory</param-value>
   </init-param>
-->
...


上面的代码在意义上类似于前面讨论的 EJB 环境参数。注释掉的 Web 部署描述符部分后面将需要用于分离容器(Separate Containers)拓扑。The Web deployment descriptor section that is commented out will be needed later for the Separate Containers topology. 如果这三个附加的参数没有注释掉,在与此体系结构有关的逻辑将通过该 Servlet 的 init() 方法进行调用。(稍后将对此进行更多的介绍。)

第二个代码片段描述了需要用来建立 Servlet 与 EJB 通信的引用:


...
<ejb-ref id="EjbRef_03">
		<ejb-ref-name>ejb/DualSessionRemoteHome</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<home>dual.ejb.DualSessionRemoteHome</home>
		<remote>dual.ejb.DualSessionRemote</remote>
		<ejb-link>DualEJBModule02.jar#DualSession</ejb-link>
</ejb-ref>
<ejb-local-ref id="EJBLocalRef_03">
		<ejb-ref-name>ejb/DualSessionLocalHome</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<local-home>dual.ejb.DualSessionLocalHome</local-home>
		<local>dual.ejb.DualSessionLocal</local>
		<ejb-link>DualEJBModule02.jar#DualSession</ejb-link>
</ejb-local-ref>
...


相对地,DualServlet 的源代码包含该 Servlet 的 init() 方法中的处理逻辑。下面是与处理 EJB 引用有关的代码片段:


...
final String JNDI_NAME_LOCAL = "java:comp/env/ejb/DualSessionLocalHome";
final String JNDI_NAME_REMOTE = "java:comp/env/ejb/DualSessionRemoteHome";
...
   if (useLocal) {
      initCtx = new InitialContext();
      homeObject = initCtx.lookup(JNDI_NAME_LOCAL);
      sessionLocalHome = (DualSessionLocalHome) homeObject;
   } else {
      homeObject = initCtx.lookup(JNDI_NAME_REMOTE);
      sessionRemoteHome =
         (DualSessionRemoteHome) PortableRemoteObject.narrow(
         homeObject,
         DualSessionRemoteHome.class);
   }
...


(下载的源代码中的初始参数处理逻辑实际上更复杂,因为它还处理分离容器(Separate Containers)拓扑。)

可以在 DualWebModule03 的 Web Deployment Descriptor 编辑器窗格中,首选选中 DualServlet 的所有设置(包括 Initialization 部分在内),然后转到 References 选项卡,选中 EJBEJB Local 这两个子选项卡。两个引用应该指向 WebSphere Bindings 部分中的JNDI 名“ejb/dual/ejb/DualSessionHome”。

项目依赖性总结
现在我们基本上完成了这些模块,让我们再次回顾一下所有项目(包括 EAR 在内)的依赖性:

  • DualJavaModule00 没有依赖性。
  • DualEJBModule01 没有依赖性。
  • DualEJBModule02 在“Java Build Path”、“Java JAR Dependencies”和“Project References”设置中必须有 DualJavaModule00DualEJBModule01 项目。
  • DualWebModule03 在“Java Build Path”、“Project References”和“Web Library Projects”设置中必须有 DualJavaModule00DualEJBModule02 项目。
  • DualEARForEJBContainer 在“Project References”设置中必须有 DualJavaModule00DualEJBModule01DualEJBModule02;EAR 部署描述符必须有指向 EJB 模块和 Java 模块的引用。
  • DualEARForAllInOne 必须有指向所有模块(包括 Java、EJB 和 Web 模块)的引用;EAR 部署描述符同样。
  • DualEARForWeb 必须有指向 DualJavaModule00DualEJBModule02DualWebModule03 模块的项目引用;EAR 部署描述符同样。

部署不同的体系结构

服务器项目

  1. 创建一个名为“TestServerTomcat”的 Tomcat 服务器项目。在此项目内,选择 New Server and Server Configuration,输入“ServerTomcat”作为服务器名(Server name),并且在“Apache Tomcat version 4.1”下选择 Test Environment 作为服务器类型(Server type)。在下一个面板中,指定 TOMCAT_HOME 作为安装 Tomcat 的目录,并且指定适当的 JDK 位置设置。请参见图 7。

    图 7. 服务器 Tomcat 设置
    服务器 Tomcat 设置

  2. 创建服务器项目“TestServerWAS”。在此项目中,创建服务器“ServerWAS”并接受所有的缺省值。(详细情况请参见 “Hello World”J2EE 应用程序文章)。

最后,您的工作台看起来应该是这样的:

图 8. 具有所有项目的工作台
具有所有项目的工作台

现在,我们可以将项目添加到服务器并测试 J2EE 应用程序。

部署 All-in-One-All-Local 体系结构
要部署这种体系结构,您需要遵循下列步骤:

  1. DualEARForAllInOne 添加到 WebSphere Application Server 配置。打开服务器配置编辑器,输入必要的设置;例如,将类路径设置为项目文件夹。由于我们稍后将进行性能测试,所以最好同时更改某些环境设置;例如,将 Java VM 参数设置为 -Xms512M -Xmx512M,禁用通用测试客户端(universal test client)等等。
  2. 更改某些数据源设置也是有必要的。现在,从提供者列表中删除“Cloudscape”,并且为“Default DB2”提供者添加版本 5.0 的数据源。接受所有的缺省值,并且指定 dualDB 作为“databaseName”资源特性值。在服务器配置编辑器的 EJB 选项卡上,将缺省数据源设置为 Data source 1
  3. 返回到 DualEJBModule01 EJB 部署描述符编辑器。对于 DualEntity Bean,将“CMP Connection Factory JNDI name”的值设置为 jdbc/ds1,并且将“Container authorization type”设置为 Per_Connection_Factory
  4. 对于初始测试,确保 DualEJBModule02web.xmlejb-jar.xml 中的“useLocal”设置为 true
  5. 发布并启动 ServerWAS
  6. 通过 Web 浏览器导航到 URL http://localhost:9080/DualWebModule03。您应该看到索引页面,带有显示的表单。

    图 9. 索引和结果页面
    索引和结果页面

  7. 现在,您可以测试整个 J2EE 应用程序,并且在 Application Developer 中观察控制台消息。

部署 All-in-One-All-Remote 体系结构
部署这种体系结构不需要更改项目设置。然而,要在相同的部署(实现 All-in-One-All-Remote)中转换到远程接口,您需要将 DualEJBModule02web.xmlejb-jar.xml 中的“useLocal”设置值更改为 false

部署分离容器(Separate Containers)体系结构
要实现这种体系结构,您需要从“ServerWAS”配置中删除 DualEARForAllInOne 项目,并且将 DualEARForEJBContainer 添加到它,然后,将 DualWebModule03 项目添加到“ServerTomcat”配置。

指示 Servlet 使用特定的 URL 来定位 EJB(通过取消 web.xml 中相关参数设置的注释)。在 ejb-jar.xml 中将“useLocal”设置为 true,以便实现 WebSphere Application Server 中的 EJB 容器内的最快通信。

在启动服务器之前,我们必须为 Tomcat 设置环境。打开 Tomcat 服务器编辑器,选择 Environment 选项卡以设置“Class Path”:

  • 添加变量:${WAS_50_PLUGINDIR}/properties
  • 添加文件夹:DualEJBModule02/ejbModule、DualJavaModule00
  • 对于 Tomcat 类加载器,我们需要 Enterprise JavaBeans 类和接口(通常包含在 javax.ejb 包内)。对于 Tomcat,将标准的 j2ee.jar 放入类路径可能会引起已知的不兼容性问题,因为 javax.servlet 包出现在 j2ee.jarservlet.jar 这两个文件中。避免这个问题的惟一方法是创建一个新的 j2ee-modified.jar,只包含必要的与 EJB 有关的类(通过下载的文件提供)。将此文件添加到“External JAR”选项中。
  • 将“Java VM Arguments”设置为 -Xms256M -Xmx256M

要启动该应用程序,您必须首先启动 ServerWAS,然后启动 ServerTomcat

将浏览器指向 http://localhost:8080/DualWebModule03 来查看索引页面,因为 Tomcat 使用端口 8080 作为 HTTP 的缺省端口。

使用简单的 Java HTTP 测试客户端进行测试
现在,我们将通过一个简单的 Java HTTP 测试客户端(通过下载的文件提供)来进行某些性能测试。测试客户端测量测试的持续时间内平均的响应时间和成功的采样数(即得到了响应的采样数)。要启动测试客户端(一个带有简单的自解释 GUI 的 Java 应用程序),可以运行 start.bat。该 URL 应该对将要测试的体系结构是有效地。选择 Set,然后选择 Start。当测试完成时,结果将显示在带颜色的文本区中。

图 10. 简单的 Java HTTP 测试客户端
简单的 Java HTTP 测试客户端

测试客户端产生指定的线程数,每个线程都通过它自己的 URL 对象提供。该客户端生成 HTTP GET 和 POST 请求,带有在 init.properties 文件中定义的参数(在 client.jar 文件中提供)。id 请求参数值是由随机数生成器计算出来的整数,而 firstNamelastName 请求参数是由从预定义的 String 数组挑选的元素创建的。该客户端使用 GET 请求来在“Set”模式下测试 URL,然后它使用 POST 请求来进行实际的性能或负荷测试。每个请求都发送一个请求,再等待响应,然后发送另一个请求到服务器,如此等等。测试结果将在下一部分进行讨论。

别忘了在启动测试之前禁用 web.xmlejb-jar.xml 中的跟踪!

性能测试结果及分析

测试设置
与开发人员的机器中的典型环境相比,测试设置中并没有什么特殊之处。我们用于测试的计算机的配置为 Intel Pentium III 处理器、1 GHz 和 1.5GB 的 RAM、Windows 2000、DB2 V8.1、WebSphere Studio Application Developer Version 5.1、Tomcat 4.1.29 和 IBM JDK 1.3.1(带有已启用的 JIT)。因此,为了进行测试设置,所有的服务和应用程序都应该已运行在相同的单处理器计算机中。

我们所进行的测试有两种类型:单线程和多线程(上述体系结构中的每一个都有 100 个线程)。为了获取可复验的结果,每种测试设置在客户端上都有一系列运行。在这些系列中第一次运行的结果被认为没有代表性,运行测试以使 JVM 和服务器“预热”。在每次测试运行之后便删除数据库 PERSON 表中的所有记录。单次测试运行一般持续的时间通常为 10 分钟。

对于分离容器(Separate Containers)体系结构,为了进行多线程测试,Tomcat 的 server.xml 中的设置需要作如下调整:


...
<Connector acceptCount="10"
      className="org.apache.coyote.tomcat4.CoyoteConnector" 
      connectionTimeout="20000"
      debug="0" enableLookups="false" 
      maxProcessors="200" minProcessors="200" 
      port="8080" redirectPort="8443" 
      useURIValidationHack="false"/>...


不需要进行重要的数据库或服务器调整。

结果
实际的测试结果显示在下表中。重要的是相对数而不是绝对数本身。一般来说,本地和远程接口的性能结果与预览有很好的一致性。(同时参见参考资料。)在我们的测试中,多线程吞吐量一般要低一些,因为每个 JVM 都必须在相同的单处理器计算机中分派多线程。对于这个样本应用程序,请求处理时间比较少,所以线程分派和时间安排相当大程度上是在整个处理时间中共享的,因而增加了响应时间。

图 11. 单线程客户端的平均响应时间
单线程客户端的平均响应时间

图 12. 多线程客户端的平均响应时间
多线程客户端的平均响应时间

图 13. 单线程客户端的吞吐量
单线程客户端的吞吐量

图 14. 多线程客户端的吞吐量
多线程客户端的吞吐量

结束语
本文展示了样本 J2EE 应用程序的设计方法、实现和测试结果,提供了 Enterprise Java Bean 的本地和远程接口使用的基本比较。公开 EJB 的本地和远程接口并不会限制 J2EE 应用程序部署中的灵活性。这两种接口使您可以改变部署场景而不用更改应用程序的源代码,并且还使您可以利用所选的任何部署体系结构。当本地接口用在相同的 JVM 中时,性能是最优的。使用分离容器(Separate Containers)体系结构性能可能会降低。对于确定的 J2EE 设计模式,我们推荐通过创建处理本地和远程接口的代码来将两种接口同时用于会话 Bean(有时用于实体 Bean),以达到最大的效率,而进行很少的额外工作。

参考资料

 

关于作者
Sheldon Wosnick  是 IBM 多伦多实验室的 WebSphere Studio Application Developer Server Tools 小组中的一名顾问软件开发人员。他和他的队员们负责 WebSphere Studio 产品家族的整个服务器运行时环境以及单元测试环境。此前,他是 VisualAge® for Java WebSphere Tools 小组的一名成员,有时候他被人们戏称为“运行时狂人”,他设计并且集成了 WebSphere 测试环境以及 Apache Tomcat 测试环境,这两个测试环境都是 VisualAge for Java 中非常受欢迎的功能。


Viktor Stepanchuk 是 IBM 多伦多实验室的软件开发人员。目前,他正设计和开发用于 Shop IBM 的组件。Viktor 从 Kiev State University(位于乌克兰基辅)获得物理和数学哲学博士学位。他还是 Sun 认证的 Java 程序员和 IBM 认证的开发人员(XML 和相关技术)。

 

 

版权所有:UML软件工程组织