EJB集群实现
EJB是J2EE技术中重要的部分,并且EJB集群是实现J2EE集群最大的挑战。
EJB技术是为分布式计算而生。它们可以在独立的服务器中运行。Web服务器组件或富客户端可以从其他的机器通过标准协议(RMI/IIOP)来访问EJB。你可以象调用你本地Java对象的方法一样调用远程EJB的方法。实际上,RMI/IIOP完全掩盖了你正在调用的对象是本地的还是远程的,这被称作本地/远程透明性。
图 16 EJB调用机制
上图显示了远程EJB的调用机制。当客户端想使用EJB,它不能直接调用,相反,客户端只能调用一个被称为“stub”的本地对象,它扮演了到远程对象代理的角色,并且有远程对象相同的接口。这个对象负责接受本地方法调用,并且这些调用通过网络代理到远程EJB。这些对象在客户JVM中运行,并且知道如何通过RMI/IIOP跨过网络查找真正的对象。要了解有关EJB更多的信息,请参考http://java.sun.com/products/ejb/
为解释EJB集群的实现,我们先看看在J2EE代码中如何使用EJB的。为了调用EJB,我们需要
l 在JNDI服务器中查找EJBHome stub
l 使用EJBHome stub查找或创建EJB对象,这样获得一个EJBObject stub
l 在EJBObject stub上调用方法
负载均衡和失效转移可以在JNDI查找时发生,这我们已在上面阐述了。在EJB stub(包括EJBHome和EJBObject)的方法调用时,供应商采用以下三种方式实现EJB负载均衡和失效转移。
智能存根(Smart stub)
正如我们所知,客户端可以通过存根对象(stub)来访门远程的EJB,这个对象可以通过JNDI树获取,甚至客户端可能透明地从WEB服务器上下载存根类文件。
这样存根就可以在运行期动态地用程序生成,而其类文件也不必在客户端环境的classpath或库中。(因为它是可以被下载的)
图 17 智能存根
如图17所示,BEA WebLogic和Jboss通过在存根代码中组合几种行为来实现EJB集群,而这些都是在客户端透明运行的(客户端不需要了解这些代码)。这种技术叫做智能存根。
智能存根确实很聪明,它包含一组它可以访问的目标实例,可以检测这些实例的任何失效,它也包含了复杂的负载均衡和失效转移的逻辑,用来分发请求到目标实例。而且,如果集群拓朴改变了的话(比如新增或删除了服务器实例),存根可以更新它的目标实例清单来反映新的拓朴,而不需要手工重新配置。
使用智能存根实现集群有以下优点:
l 因为EJB存根运行在客户端,所以它可以节省许多服务器资源。
l 负载均衡是在客户端代码中,并且与客户端的生命周期相关。这样就消除了负载均衡器的单点失效。如果负载均衡器失效了,就意味着客户端也失效了,这种情况是能接受的。
l 存根可动态的自动下载更新,这意味着零维护。
IIOP运行期库
Sun JES Application Server使用了另一种方法实现EJB集群。负载均衡和失效转移逻辑是在IIOP运行库中实现的。比如,JES修改了“ORBSocketFactory”的实现,使它能支持集群。如图18所示。
图 18 IIOP运行期
“ORBSocketFactory”的修改版有实现负载均衡和失效转移的所有逻辑和算法,这样就保持了存根干净和小。因为是在运行库中实现的,这样就比存根的方式更容易获得系统资源。但这种方式需要在客户端运行一个特殊的运行库,这样就给与其他J2EE产品进行互操作时带来了麻烦。
拦截器代理
IBM Webshpere采用了定位服务精灵(Location Service Daemon-LSD),它对EJB客户端扮演了拦截器代理的角色,如图19所示。
图 19 拦截器代理
在这种方式中,客户端通过JNDI查找获取存根,这个存根将信息路由到LSD,而不是运行了EJB的应用程序服务器。这样LSD接收到所有进来的请求,根据负载均衡和失效转移的策略来判断应该将它发送到哪个服务器实例。这种方式增加的安装和维护集群的额外的管理工作。
EJB的集群支持
要调用EJB的方法,要涉及到两种存根对象,一是EJBHome接口,另一个是EJBObject接口。这意味着EJB潜在的需要在两层上实现在负载均衡和失效转移。
l 当客户端使用EJBHome存根创建或查找EJB对象时
l 当客户端调用EJB对象上的方法时。
EJBHome存根支持集群
EJBHome接口用于在EJB容器中创建和查找EJB实例,而EJBHome存根是EJBHome接口的客户端代理。EJBHome接口不需维持任何客户端的状态信息。这样,来自不同EJB容器中的相同EJBHome接口对于客户端来说是一致的。当客户端执行create()或find()调用的时候,home存根根据负载均衡和失效转移算法从多个相同的服务器实例中选择一个,并将调用发送到该服务器的home接口上。
EJBObject存根支持集群
当EJBHome接口创建一个EJB实例,它将返回EJBObject的存根给客户端,并让用户调用EJB的业务方法。集群中已有一组可用的服务器实例,并在上面的部署了bean,但是不能将EJBObject存根对象向EJBObject接口发出调用转发到任意一个服务器实例上,这取决于EJB的类型。
无状态会话Bean是最简单的情况,因为不涉及到状态,所有的实例都可以认为是一样的,这样对EJBObject的方法调用可负载均衡和失效转移到任意一个服务器实例上。
集群的有状态会话Bean与无状态会话Bean有一点不同,正如我们所知,有状态会话Bean对于客户端连续的请求会持有状态信息。从技术上来说,集群有状态会话Bean与集群HTTP Session是一样的。在常规时间,EJBObject存根将不会把请求负载均衡到不同的服务器实例。相反,它将胶粘到最初创建的服务器实例上,我们称这个实例为“主实例”。在执行过程中,会话状态会从主实例备份到其他服务上去。如果主实例失效了,备份服务器将接管它。
实体Bean本质上是无状态的,尽管它可以处理有状态的请求。所有的信息都将通过实体Bean本身的机制备份到数据库中。这样看起来实体Bean可以象无状态会话Bean一样很容易获取负载均衡和失效转移。但实际,实体Bean多数情况下是不负载均衡和失效转移的。根据设计模式的建议,实体Bean被会话外观包装。这样,多数对实体Bean的访问都是进程内会话Bean通过本地接口完成的,而不是远程客户端。这使得负载均衡和失效转移没有意义。
JMS和数据库连接的集群支持
除JSP,Servlet,JNDI和EJB之外,在J2EE中还有其他的分布式对象。这些对象在集群的实现中可能支持,可能不支持。
当前,一些数据库产品,如Oracle RAC,支持集群环境并可以部署到多复制,同步的数据库实例中。然而,JDBC是一个高度状态化的协议并且它的事务状态直接与客户端和服务器的Socket连接绑定,所以很难获取集群能力。如果一个JDBC连接失效了,与该失效连接相关的所有JDBC对象也就失效了。客户端代码需要进行重连的动作。BEA WebLogic使用JDBC多池(multipool)技术来简化这种重连过程。
JMS被多数J2EE服务器所支持,但支持得并不完全,负载均衡和失效转移仅仅被JMS代理所实现,很少有产品在JMS Destination中的消息有失效转移的功能。