java集群技术(转)

来源:http://blog.csdn.net/cdh1213/article/details/21443239html

序言java

愈来愈多的关键应用运行在J2EE(Java 2, Enterprise Edition)中,这些诸如银行系统和帐单处理系统须要高的可用性(High Availability, HA),同时像Google和Yahoo这种大系统须要大的伸缩性。高可用性和伸缩性在今天高速增加的互链接的世界的重要性已经证明了。eBay于 1999年6月停机22小时的事故,中断了约230万的拍卖,使eBay的股票降低了9.2个百分点。mysql

J2EE集群是用来提供高可用性和伸缩性服务,同时支持容错处理的一种流行的技术。可是,因为J2EE规范缺少对集群的支持,J2EE供应商实现集群的方法也各异。这给J2EE架构师和开发人员带来了不少困难。如下是几个常见的问题:linux

  • 为何带集群功能的商业J2EE服务器产品如此昂贵?(10倍于不带集群功能的产品)
  • 为何基于单服务器环境构建的应用不能在集群中运行?
  • 为何应用在集群环境中运行得很慢,但在非集群环境中却快得多?
  • 为何集群的应用移植到其余服务器中失败?

理解这些限制和要素的最佳方法是学习他们的实现方式。算法

基本术语sql

在咱们讨论不一样的集群实现以前,先谈谈几个概念。这有助于理解不一样的J2EE集群产品不一样的设计结果和概念:数据库

伸缩性(Scalability):设计模式

在一些大的系统中,预测最终用户的数量和行为是很是困难的,伸缩性是指系统适应不断增加的用户数的能力。提升这种并发会话能力的一种最直观的方式就增长资源(CPU,内存,硬盘等),集群是解决这个问题的另外一种方式,它容许一组服务器组在一块儿,像单个服务器同样分担处理一个繁重的任务。浏览器

高可用性(High availability):缓存

单一服务器的解决方案并非一个健壮方式,由于容易出现单点失效。像银行、帐单处理这样一些关键的应用程序是不能容忍哪怕是几分钟的死机。它们须要这样一些服务在任什么时候间均可以访问并在可预期的合理的时间周期内有响应。集群方案经过在集群中增长的冗余的服务器,使得在其中一台服务器失效后仍能提供服务,从而得到高的可用性。

负载均衡(Load balancing):

负载均衡是集群的一项关键技术,经过把请求分发给不一样的服务器,从而得到高可用性和较好的性能。一个负载均衡器能够是从一个简单的Servlet或 Plug-Ins(例如一个Linux box利用ipchains来实现),到昂贵的内置SSL加速器的硬件。除此以外,负载均衡器还需执行一些其余的重要任务,如“会话胶粘”让一个用户会话始终存在一个服务器上,“健康检查”用于防止将请求分发到已失效的服务器上。有些负载均衡器也会参与咱们下面将要谈到“失效转移”过程。

容错(Fault tolerance):

高可用性意味着对数据正确性的要求不那么高。在J2EE集群中,当一个服务器实例失效后,服务仍然是有效的,这是由于新的请求将被冗余服务器处理。可是,当一个请求在一个正在失效的服务器中处理时,可能获得不正确的结果。无论有多少个错误,容错的服务应当能确保有严格的正确的行为。

失效转移(Failover):

失效转移是集群中用来获取容错能力的另外一项关键的技术。当一个结点失效后,经过选择集群中的另外一个结点,处理将会继续而不会终止。转移到另外一个结点能够被显式的编码,或是经过底层平台自动地透明地路由到另外一个服务器。

等幂方法(Idempotent methods):

等幂方法是指这样一些方法:重复用相同的参数调用都能获得相同的结果。这些方法不会影响系统状态,能够重复调用而不用担忧改变系统。例如:getUsername()就是等幂的,而deleteFile就不是。当咱们讨论HTTP Session失效转移和EJB失效转移时,它是一个重要的概念。

什么是J2EE集群

一个天真的问题,不是吗?但我仍要用几句话和图来回答它。一般,J2EE集群技术包括"负载均衡"和"失效转移"。

 

 
图 1  负载均衡

如图1所示,负载均衡意味着有许多客户端向目标对象同时发出请求。负载均衡器在调用者和被调用者之间,分发请求到与原始对象相同的冗余对象中。伸缩性和高可用性就是这样获得的。

 

 
图 2  失效转移

如图2所示,失效转移与负载均衡不一样。有时客户端会连续发请求到目标对象,若是请求中间目标对象失效了,失效转移系统将检测到此次失败,并将请求重定向到另外一个可用的对象。经过这种方式能够得到容错能力。

若是你想知道更多的有关J2EE集群的知识,你就会问到一个基本的问题,“什么对象能够集群?”和“在个人J2EE代码中哪里会发生负载均衡和失效转移呢?”。这些都是用来理解J2EE集群的很是好的问题。实际上,并非全部的对象都能被集群的,而且负载均衡和失效转移并非在J2EE代码全部地方都能发生。看看下面的例子代码:

 

 
图 3  例子代码

在Class A的bussiness()方法中,instance1能够负载均衡吗?或是当其失效,能够失效转移到其余B的实例上吗?我想是不行的!对负载均衡和失效转移来讲,必需要有个拦截器在调用者和被调用者之间分发或重定向请求到不一样的对象上。Class A和Class B的实例是运行在一个JVM中紧密耦合的,在方法调用间加入分发逻辑很是困难。

什么类型对象能够被集群?——只有那些能够被部署到分布式拓朴结构中的组件。

在个人J2EE代码中,什么地方会有负载均衡和失效转移?——只在你调用分布式组件的方法时。

 

 
图 4  分布式对象

在如图4所示的分布式环境中,调用者和被调用者被分离在有明显边界的不一样的运行容器中,这个边界能够是JVM,进程和机器。

当目标对象被客户端调用时,目标对象的功能是在容器中运行的(这就是为何咱们说它是分布式的缘由)。客户端和目标对象经过标准的网络协议通讯。这些特性就为一些机制提供了机会能够介入到方法调用之间实现负载均衡和失效转移。

如图4,浏览器经过HTTP协议调用JSP对象,JSP运行在WEB服务器中,浏览器只须要返回结果而不关心它是怎么运行的。在上述场景中,一些东西就能够在浏览器与WEB服务器之间实现负载均衡和失效转移的功能。在J2EE平台,分布式技术包括:JSP(Servlet),JDBC,EJB,JNDI,JMS,WEB Service等。负载均衡和失效转移就发生在这些分布式方法被调用时。在后续部分咱们将详细讨论这些技术。

 

4 WEB层集群实现
WEB层集群是J2EE集群的重要且基本的功能。WEB集群技术包括WEB负载均衡和HTTP Session失效转移。

4.1 WEB负载均衡 
J2EE提供商实现WEB负载均衡有许多方式。基本上,都一个负载均衡器被插入到浏览器和WEB服务器之间,以下图所示。

 

 
图 5  WEB负载均衡

负载均衡器能够是一台硬件,如F5负载均衡器,或仅仅是另外一台有负载均衡Plug-Ins的WEB服务器,一个简单的带ipchains的Linux box能够很好的实现负载均衡。无论采用哪一种技术,负载均衡器都有如下特性:

4.1.1 实现负载均衡算法

当客户请求到来时,负载均衡器须要决定将如何分发到后台服务器。流行的算法是Round-Robin、Random和Weight Based。负载均衡器尽力使每台服务器实例都得到相同的负载,可是上述算法没有一个能够得到理想的负载相同,由于它们仅仅是依据发送到特定服务器的请求的个数。一些精密的负载均衡器实现了特殊的算法。它在分发请求以前将检测服务器的工做负载。

  • 健康检测

当一台服务器失效了,负载均衡器应当检测出失效并再也不将请求分发到这台服务器上。一样,它也要检测服务器是否恢复正常,并恢复分发请求。

  • 会话胶粘

几乎全部的WEB应用程序都有一些会话状态,多是简单的记住用户是否登录,或是包含你的购物车信息。由于HTTP自己是无状态的,会话状态应当存在服务器的某个地方并与你当前浏览会话相关联,这样当你下次再请求相同WEB应用程序的页面时能够很容易的从新获取。当负载均衡时,最佳的选择就是将特定的浏览器会话分发到上次相同的服务器实例中,不然,应用程序可能不能正确工做。

由于会话状态存储在特定WEB服务器的内存中,“会话胶粘”对于负荷均衡很是重要。可是,若是其中某台服务器实例由于某种缘由失效了(好比关机),那么这台服务器的会话状态将要丢失。负载均衡器应当检测到这个失效并再也不将请求分发给它,但这些请求的会话状态都由于存放在失效的服务器中而丢失了全部信息,这就将致使错误。会话的失效转移所以而生。

4.2 HTTP Session失效转移 
几乎全部流行的J2EE供应商都在他们的集群产品中实现了Http Session失效转移,用来保障当某台服务器失效后会话状态不会丢失,使客户端请求能被正确处理。如图6所示,当浏览器访问有状态的WEB应用程序(第 1 ,2步),这个应用程序可能在内存建立了会话对象用于保存信息以供后面的请求使用,同时,发送给浏览器一个惟一的HTTP Session ID用于标识这个会话对象(第3步),浏览器将这个ID保存Cookie中,并当它下次再请求同一WEB应用程序的页面时,会将Cookie发还给服务器。为了支持会话失效转移,WEB服务器将在必定的时候把会话对象备份到其余地方以防止服务器失效后丢失会话信息(第4步)。负载均衡器检测到这个失败(第5,6步),并将后续的请求分发到装有相同应用程序的服务器实例中(第7步),因为会话对象已经备份到其余地方了,这个新的服务器实例能够恢复会话(第8步)正确地处理请求。

 

 
图 6  HTTP Session失效转移

为了实现上述功能,HTTP Session失效转移将带来如下问题:

  • 全局HTTP Session ID

如上所述,HTTP Session ID用于在特定的服务器实例中标识惟一的内存会话对象,在J2EE平台,HTTP Session ID依赖于JVM实例,每一个JVM实例拥有几个应用程序,每一个应用程序都为不一样的用户管着许多会话,HTTP Session ID是在当前JVM实例用于访问相关会话的关键。在会话失效转移的实现中,要求不一样的JVM实例不能产生两个相同的HTTP Session ID,这是由于当失效转移发生时,一个JVM的会话将要备份并恢复到另外一个中,这样,必须创建全局HTTP Session ID产生机制。

  •  如何备份会话状态

如何备份会话状态是区别J2EE服务器好坏的关键因素。不一样的供应商有不一样的实现,在后续部分我再详细解释。

  • 备份的频率和粒度

会话的备份是消耗性能的,包括CPU,内存,网络带宽和写入磁盘和数据库的I/O,备份的频率和备份对象的粒度将严重影响性能。

4.2.1 数据库备份方式

几乎全部的J2EE集群产品都容许选择将你的会话对象经过JDBC备份到关系数据库中。如图7所示,这种方式可让服务器实例很是简单的在正确的时间序列化会话内容并写到数据库中。当发生会话转移时,另外一台可用的服务器接过已失效的服务器工做,从数据库中恢复全部的会话状态。序列化对象是关键点,它使得内存会话数据能够持久化和传输。要了解更多有关Java对象序列化知识,请参考http://java.sun.com/j2se/1.5.0/docs/guide/serialization/index.html 。

 

 
图 7  备份会话数据到数据库

因为数据库交易是很是昂贵的,这种方法主要缺点是当在会话中保存大量的或大的对象时限制了伸缩性,大多数使用数据库会话持久化的服务器产品都提倡尽可能减小用HTTP会话存储对象,但这限制了你的应用程序的架构和设计,特别是你要使用HTTP会话缓存用户数据时。

数据库的方式也有一些优势:

  • 简单,容易实现。分离的请求处理和会话备份处理使集群更好管理和健壮。
  • 会话能够失效转移到任何一台服务器,由于数据库是共享的。
  • 当整个集群失效时,会话数据依旧幸免。

4.2.2 内存复制方式 
由于性能的缘由,一些J2EE服务器(Tomcat,Jboss,WebLogic,WebSphere)提供了另外一种实现:内存复制

 

 
图 8  对会话状态进行内存复制

基于内存的会话持久化将会话信息保存在一台或是多台备份服务器中,而不是保存数据库中(如图8)。这种方式由于性能高而很是流行。同数据库方式相比,直接在原服务器和备份服务器之间网络通讯是很是轻量的。同时注意在使用方式中,数据库方式中的“恢复”阶段是不须要的,由于在备份后,全部会话数据都已经存在备份服务器的内存中了,已经能够处理请求。

“Java Groups”是当前Tomcat和Jboss集群所使用的通讯层。Java Groups是用于实现可靠组通讯和管理的工具包。它提供了诸如“组成员协议”和“消息广播”等核心特性,这些都对集群的工做很是有用。有关Java Groups的信息,请参考:http://www.jgroups.org/javagroupsnew/docs/index.html 。

4.2.3 Tomcat方式:多服务器复制

内存复制也存在许多不一样的方式,第一种方法就是将会话数据复制到集群中的全部结点,Tomcat5采用的就是这种方式。

 

 
图9  多服务器复制

如图9所示,当一个服务器实例的会话改变后,将备份到其余全部的服务器上。当一台服务器失效后,负载均衡器能够选择其余任何一台可用的服务器实例。但这种方式限制了伸缩性,若是集群中有不少的服务器实例,那么网络通讯的代价就不能被忽略,这将严重下降性能,而且网络也将成为系统的瓶颈。

4.2.4 WebLogic,Jboss和Websphere的方式:对等服务器复制

因为性能和伸缩性的缘由,WebLogic,Jboss和Webshpere采用了其余方式实现内存复制。每台服务器任意选择一台服务器备份其内存中的会话信息。如图10所示。

在这种方式中,每台服务器都有一台本身的对等服务器,而不是其余全部的服务器,这种方式消除在集群中加入过多服务器实例的话影响伸缩性的问题。

 

 
图 10  对等服务器复制

尽管这种方式实现失效转移有很高的性能和伸缩性,但它仍有一些限制:

  • 它给负载均衡器带来了更多的复杂性。当一台服务失效后,负载均衡器必须知道那台服务是这台己失效服务器的对等备份服务器。这将缩小了负载均衡器的选择范围,同时有些硬件也不能知足这种要求。
  •  除了处理正常的请求外,服务器还将负责复制的任务。因为备份会话数据的任务也须要占用CPU的周期,因此每台服务器的请求处理能力也下降了。
  • 在没有发生失效转移的时候,备份服务器上大量用于备份的内存是个浪费。同时这也将增长了JVM GC的负担。
  • 集群中的服务器实例构成了复制对。这样,当会话所在主服务器失效后,负载均衡器将会话转移到备份服务器,使备份服务器处理两倍的请求,这将形成备份服务器的性能问题。

为了克服上面的4点问题,不一样的软件供应商采用了不一样的方法,WebLogic采用的复制对不是对每台服务器,而是对每一个会话。当一台服务器实例失效后,会话数据己经分散备份到多个备份服务器上,使失效的负载均匀地分布。

4.2.5 IBM的方式:中央状态服务器

Websphere采用不一样的方式实现内存复制:备份会话信息到中央的状态服务器,如图11所示:

 

 
图 11  中央状态服务器复制

这与数据库的解决方案很是相似,不一样之处在于专用的“会话备份服务器”代替了数据库服务器,这种方式结合了数据库和内存复制两种方式的优势。

  • 将请求处理和会话备份处理分开使用集群更加健壮。
  •  全部的会话数据都备份到专用的服务器上,无需服务器浪费内存用于备份其余服务器的会话。
  • 由于会话备份服务器是在服务器之间共享的,全部失效后能够转移到任何一台服务器上,这样大多数据软硬件负载均衡器均可以使用,更重要的是当一台服务器失效后,负载将均匀的分布到全部实例上。
  • 与重量级的数据库链接相比,应用服务器与备份服务器之间Socket通讯是轻量的。这样就比数据库的解决方案有更好的性能和更好的伸缩性。

然而,因为有恢复失效服务器会话数据的这么一个阶段,所以其性能确定不如两台服务器直接复制解决方案,另外,多出来一台备份服务器也增长了管理的复杂性。也可能因为单台备份服务器形成性能瓶颈。

4.2.6 Sun的方式:特殊数据库

 

 
图 12  特殊数据库复制

Sun JES应用服务器采用了别的方式实现会话失效转移,如图12所示,它看上去很像数据库的方式,由于它采用关系数据库存储会话并经过JDBC访问全部会话数据。可是JES内部所使用的关系数据库称为HADB,已经为访问会话作了特别优化,而且将几乎全部的会话数据存在内存中。这样,你能够说它更像中央状态服务器的方式。

4.2.7 性能因素

考虑以下问题:一台WEB服务器中可能运行着许多WEB应用,它们中每个均可能被成百的并发用户访问,而每一个用户都会产生浏览器会话用于访问特定的应用。全部会话信息都将备份以便服务器失效后能转移到其余服务器实例中。更糟的是,会话会因为一次次的发生如下状况而变化,包括建立、失效、增长属性、删除属性、修改属性值。甚至是什么都没变,但因为有新的访问而使最后访问时间变了(由此判断何时失效会话)。所以,性能在会话失效转移的解决方案中是个很大的因素。供应商一般会提供一些可调的参数改变服务器行为,使之适应性能需求。

4.2.7.1 备份时机

当客户端的请求被处理后,会话随时改变。因为性能因素,实时备份会话是不明智的。选择备份频率须要平衡。若是备份动做发生得太频繁,性能将急剧降低。若是两次备份的间隔太长,那么在这间隔之间服务器失效后,不少会话信息将会丢失。无论全部的方式,包括数据库和内存复制,下面是决定备份频率的经常使用的选项。

  •  按WEB请求

在WEB请求处理结束后,发生响应以前保存数据。这种方式可以保证在失效后备份的会话数据是最新的。

  • 按固定的时间间隔

会话在后台按固定的时间间隔保存。这种方式不能保证备份的会话数据是最新的。但因为不需在每次请求以后备份数据,于是有更好的性能。

4.2.7.2 备份粒度

当备份会话的时候,你还须要决定多少会话状态须要保存。如下是不一样产品全部采用的经常使用的策略。

  • 整个会话

每次都将保存全部会话。这种方式为正确保存分布式WEB应用的会话提供了最好保证。这种方式简单,内存复制和数据库存储方式都默认采用这种方式。

  • 修改过的会话

当会话被修改事后,则备份整个会话。当“session.setAttribute()”或 “session.removeAttribute()”被调用后,则认为会话被修改过。必须保证这些方法调用才修改会话属性,这并非J2EE规范后要求的。但倒是正确实现这种方法所须要的。备份修改过的会话减小了会话存储的数量,那些仅仅是读取会话属性的请求将不会触发会话备份的动做。这将带来比备份整个会话更好的性能。

  • 修改过的属性

仅仅是保存被修改过的会话属性而不是整个会话。这将最小化备份的会话数据。这种方式带来最好的性能及最小的网络通讯。为了保证这种方式工做的正确性,必须依据如下的要点。首先,只能经过调用“setAttribute()”方法修改会话状态,而且会话数据要被序列化和备份。其次,确保属性之间没有交叉引用。每一个惟一的属性键值下的对象图应当被独立地序列化和保存。若是两个独立的键值下的对象存在交叉引用,它们将不可以正确的序列化和反序列化。例如图13所示,在一个内存复制的集群中,会话中存有“student”和“school”对象,同时“school”对象含有到“student”对象的引用,某一个时候“school”被修改后备份到备份服务器中。在序列化和反序列化以后,在备份服务器的被还原的“school”对象的版本将包含整个对象图,包括其引用的“student”对象。可是“student”对象能够被独立的修改,当它被修改后,仅仅只有它本身被备份。在序列化和反序列化以后,在备份服务器中还原“student”对象,但在此时,它将丢失与“school”对象的链接。尽管这种方式有最好的性能,但上述限制将影响WEB应用程序的架构和设计。特别是须要用会话保存缓存的复杂的用户数据。

 

 
图 13  会话复制中的交叉引用

4.2.8 其余失效转移的实现

如前面章节所提到的,当会话备份时粒度对于性能很是重要。然而,当前的一些实现,无论是数据库存储仍是内存复制,都将使用Java对象序列化技术来传输Java对象。这就打了个大印子,影响系统的性能,并给WEB应用程序的架构和设计带来不少的限制。一些J2EE供应商便寻找一些特别的手段来更为轻量地,小印子地实现WEB集群,提供细粒度的分布式对象共享机制用于提升集群的性能。

4.2.8.1 Jrun的Jini技术

Jrun4将它的集群解决方案构在Jini技术之上。Jini为分布式计算而生,它容许在一个单一的分布式计算空间内建立“联合”的设备或组件。 Jini提供查找,注册,租用等分布式系统服务,这对集群环境很是有用。另外一种称为JavaSpace的技术构建于Jini之上,它提供了一些用于实现集群很是有价值的特性,如对象处理,共享,迁移等。要了解有关Jini和JavaSpace更多的信息,请参考http://java.sun.com/products/jini/2_0index.html

4.2.8.2 Tangosol的分布式缓存

Tangosol Coherence提供了一个分布式数据管理平台,它能够嵌入到大多数流行的J2EE服务器中用于实现集群环境。Tangosol Coherence同时也是提供了分布式缓存系统用于在不一样的JVM之间有效地共享Java对象。要了解有关Tangosol更多的信息,请参考http://www.tangosol.com

 

5 JNDI集群实现

J2EE规范要求全部的J2EE容器必须提供JNDI规范的实现。JNDI在J2EE应用程序中的主要角色是用来提供一个间接层,这样资源能够很容易被找到,而不用关心细节。这使得J2EE组件更加可复用。

拥用全特性的集群的JNDI对于J2EE集群是很是重要的。全部的EJB调用都开始于在JNDI树上查找它的Home接口,J2EE供应商根据他们的集群结构采用不一样的方式实现JNDI集群。

5.1 共享全局JNDI树 
WebLogic和Jboss都有一个全局的,共享的,集群范围的JNDI Context供客户端查找和绑定对象,绑定的全局JNDI Context中对象将经过IP广播的方式在集群中复制,这样当一台服务器实例停机后,被绑定的对象仍然可供查找。

 
图 14  共享的全局JNDI

如图14所示,共享的全局JNDI树实际包括了全部本地JNDI结点上绑定的对象。集群上每一个结点都拥有本身的名称服务器,它们与集群中其余服务器相互复制全部的东西。这样每一个名称服务器上都拥有其余名称服务器对象树的拷贝。这种冗余结构使得全局JNDI树高可用。

实际上,集群的JNDI树能够被用作两个目的。能够被管理员用来部署对象和服务。在一台服务中部署完EJB模块或JDBC&JMS服务后,JNDI树上的全部对象都将复制到其余服务器实例中。在运行期,程序能够JNDI API访问JNDI树存储或者获取对象,这些对象也将被全局复制。

5.2 独立JNDI
Jboss和WebLogic都采用了共享全局JNDI树的方式,Sun JES和IBM WebSphere等采用了每一个服务器都拥有独立的JNDI树的方式。在使用独立JNDI树的集群中,成员服务器没必要知道或关心集群中其余服务器。这是否意味着不想把JNDI集群呢?全部EJB访问都开始于在JNDI树上查找它们的Home接口,若是没有可集群的JNDI树,集群就根本无用。

实际上,若是J2EE应用程序是类似的,独立的JNDI树仍然能够得到高可用性。当集群中全部实例都有相同的设置以及都部署相同的应用程序集,咱们说集群是“类似的”,这种状况下,一种被称为代理的特殊管理工具能够帮助咱们获取高可用性,如图15所示:

 

 
图 15  独立JNDI

无论是Sun JES仍是WebSphere都在集群的实例上安装告终点代理,当部署EJB模块或绑定其余JNDI服务,管理控制员能够向全部的代理发出命令,以此实现与共享全局JNDI相同的效果。

可是独立JNDI的方案不能支持复制在运行期绑定或获取的对象。有如下几个缘由:JNDI在J2EE应用程序中的主要角色是用来提供管理外部资源的间接层,并非用来作数据存储。若是有这样的需求,能够采用具备HA(高可用性)特性的独立的LDAP服务器或数据库。Sun和IBM本身都有这样拥有集群特性的独立的LDAP服务器产品。

5.3 中心JNDI
少数J2EE产品使用了中心JNDI方案,这种方案中名称服务器驻留在单一服务器中,全部的服务器实例都注册它们相同的EJB组件和管理对象到单一的服务器中。

名称服务器实现了高可用性,这对客户端是透明的。全部的客户端都在这台服务器中查找EJB组件,可是这种结构意味着复杂的安装和管理,慢慢被多数供应商抛弃。

5.4 初始化访问JNDI服务器 
固然客户端要访问JNDI服务器的时候,它们须要知道远程JNDI服务器的主机名/IP地址和端口,在全局或是独立JNDI树的方案中,有多个JNDI服务器。客户端第一次访问时应该链接哪一个呢?如何得到负载均衡和失效转移呢?

从技术上说,一个软件或硬件负载均衡器能够设在远程客户端和全部的JNDI服务器之间执行负载均和失效转移。可是,不多有供应商实现这种方式,这里有些简单的方案:

  • Sun JES和Jboss 实现JNDI集群是在“java.naming.provider.url”属性中设置一列用逗号分隔的URL,如 java.naming.provider.url=server1:1100,server2:1100:server3.1100:server4.1100 客户端将挨个联系列表中的服务器,一旦其中一个响应了便停止。
  • Jboss同时也实现了自动发现的特性,当设置属性串“java.naming.provider.url”为空时,客户端将试图经过网络广播来发如今一个JNDI服务器。

6 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,咱们须要

  • 在JNDI服务器中查找EJBHome stub
  • 使用EJBHome stub查找或建立EJB对象,这样得到一个EJBObject stub
  • 在EJBObject stub上调用方法

负载均衡和失效转移能够在JNDI查找时发生,这咱们已在上面阐述了。在EJB stub(包括EJBHome和EJBObject)的方法调用时,供应商采用如下三种方式实现EJB负载均衡和失效转移。

6.1 智能存根(Smart stub)
正如咱们所知,客户端能够经过存根对象(stub)来访门远程的EJB,这个对象能够经过JNDI树获取,甚至客户端可能透明地从WEB服务器上下载存根类文件。

这样存根就能够在运行期动态地用程序生成,而其类文件也没必要在客户端环境的classpath或库中。(由于它是能够被下载的)

 
图 17  智能存根

如图17所示,BEA WebLogic和Jboss经过在存根代码中组合几种行为来实现EJB集群,而这些都是在客户端透明运行的(客户端不须要了解这些代码)。这种技术叫作智能存根。

智能存根确实很聪明,它包含一组它能够访问的目标实例,能够检测这些实例的任何失效,它也包含了复杂的负载均衡和失效转移的逻辑,用来分发请求到目标实例。并且,若是集群拓朴改变了的话(好比新增或删除了服务器实例),存根能够更新它的目标实例清单来反映新的拓朴,而不须要手工从新配置。

使用智能存根实现集群有如下优势:

  • 由于EJB存根运行在客户端,因此它能够节省许多服务器资源。
  • 负载均衡是在客户端代码中,而且与客户端的生命周期相关。这样就消除了负载均衡器的单点失效。若是负载均衡器失效了,就意味着客户端也失效了,这种状况是能接受的。
  • 存根可动态的自动下载更新,这意味着零维护。

6.2 IIOP运行期库 
Sun JES Application Server使用了另外一种方法实现EJB集群。负载均衡和失效转移逻辑是在IIOP运行库中实现的。好比,JES修改了“ORBSocketFactory”的实现,使它能支持集群。如图18所示。

 
图 18  IIOP运行期

“ORBSocketFactory”的修改版有实现负载均衡和失效转移的全部逻辑和算法,这样就保持了存根干净和小。由于是在运行库中实现的,这样就比存根的方式更容易得到系统资源。但这种方式须要在客户端运行一个特殊的运行库,这样就给与其余J2EE产品进行互操做时带来了麻烦。

6.3 拦截器代理
IBM Webshpere采用了定位服务精灵(Location Service Daemon-LSD),它对EJB客户端扮演了拦截器代理的角色,如图19所示。


 

图 19  拦截器代理

在这种方式中,客户端经过JNDI查找获取存根,这个存根将信息路由到LSD,而不是运行了EJB的应用程序服务器。这样LSD接收到全部进来的请求,根据负载均衡和失效转移的策略来判断应该将它发送到哪一个服务器实例。这种方式增长的安装和维护集群的额外的管理工做。

6.4 EJB的集群支持 
要调用EJB的方法,要涉及到两种存根对象,一是EJBHome接口,另外一个是EJBObject接口。这意味着EJB潜在的须要在两层上实如今负载均衡和失效转移。

  • 当客户端使用EJBHome存根建立或查找EJB对象时
  •  当客户端调用EJB对象上的方法时。

6.4.1 EJBHome存根支持集群

EJBHome接口用于在EJB容器中建立和查找EJB实例,而EJBHome存根是EJBHome接口的客户端代理。EJBHome接口不需维持任何客户端的状态信息。这样,来自不一样EJB容器中的相同EJBHome接口对于客户端来讲是一致的。当客户端执行create()或find()调用的时候,home存根根据负载均衡和失效转移算法从多个相同的服务器实例中选择一个,并将调用发送到该服务器的home接口上。

6.4.2 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中的消息有失效转移的功能。

 

8 J2EE集群的神话

8.1 失效转移能够彻底避免错误——否认

在Jboss的文档中,整个章节都在警告你“你真的须要HTTP会话的复制吗?”。是的,有时没有失效转移的高可用性的解决方案也是可接受而且是廉价的。失效转移并非你想象的那么强壮。

那么失效转移到底给你带来了什么?你可能想失效转移能够避免错误。你看,没有会话的失效转移,当一个服务器实例失效后,会话数据将丢失而致使错误。经过失效转移,会话能够从备份中恢复,而请求能够被其余服务器实例所处理,用户根本意识不到失效。这是事实,但这是有条件的!

回想同样咱们定义的“失效转移”,咱们定义了一个条件是失效转移是在“两个方法调用之间”发生的。这是说若是你有两个连续的对远程对象的方法调用,失效转移只会在第一调用成功后而且第二调用的请求发出前才能发生。

这样,当远程服务器在处理请求的过程当中失效了会发生什么呢?答案是:多数状况处理将会中止而客户端将会看到错误信息。除非这个方法是等幂的(Idempotent),只有这个方法是等幂的,一些负载均衡器更智能,它会重试这些方法并将它失效转移到其余实例上。

为何“等幂”重要呢,由于客户端不知道当失效发生的时候请求被执行到什么地方。是才刚刚初始化仍是差很少就要完成了?客户端无法判断!若是方法不是等幂的,在相同方法上的两次调用可能会两次修改系统的状态,而使得系统出现不一致的情形。

你可能想全部在事务中的方法都是等幂的,毕竟,若是错误发生,事务将被回滚,事务状态的改变都将被复位。但事实上事务的边界可能不包括全部的远程方法调用过程。若是事务已经在服务器上提交了而返回给客户端时网络崩溃怎么办呢?客户端不知道服务器的事务是不是成功了。

在一些应用程序中,将全部的方法都作成等幂的是不可能的。这样,你只能经过失效转移减小错误,而不是避免它们。拿在线商店为例,假设每台服务器能够同时处理100个在线用户的请求,当一台服务器失效了,没有失效转移的解决方案将丢失100个用户的会话数据并激怒这些用户。而有失效转移的解决方案中,当失效发生的时候有20个用户正在处理请求,这样20个用户将被失效激怒。而其余80个用户正处于思考时间或在两个方法调用之间,这些用户能够透明地得到失效转移。这样,你就需作如下的考虑:

  • 激怒20个用户和激怒100个用户形成影响的区别。
  • 采用失效转移和不采用失效转移产品成本的区别

8.2 独立应用能够透明的迁移到集群结构中——否认

尽管一些供应商宣称他们的J2EE产品有这样的灵活性。不要相信他们!事实你要在开始系统设计时就要准备集群,而这将影响开发和测试的全部阶段。

8.2.1 Http Session

在集群环境中,如我前面提到的,使用HTTP Session有不少限制,这取决于你的应用程序服务器采用了那种会话失效转移的机制。第一个重要的限制就是全部保存的HTTP Session中的对象必须是可序列化的,这将限制设计和应用程序结构。一些设计模式和MVC框架会用HTTP Session保存一些不序列化的对象(如ServletContext,EJB本地接口和WEB服务引用),这样的设计不能在集群中工做。第二,对象的序列的反序列化对性能的影响很大,特别是数据库保存的方式。在这样的环境中,应该避免在会话中保存大的或是众多的对象。若是你采用了内存复制的方式,如前所述你必须当心在会话中属性的交叉引用。其余在集群环境中的主要区别是在会话无论任何属性修改,你必须调用“setAttribute()”方法。这个方法调用在独立的系统中是可选的。这个方法的目的是区别已修改的属性和那些没用到属性,这样系统能够只为失效转移备份必要的数据,从而提升性能。

8.2.2 缓存

我经历过的大多数J2EE项目都用了缓存来提升性能,同时流行的应用程序服务器也都提供了不一样程度的缓存用来加快应用程序的速度。但这些缓存都是为那些典型的独立环境设计的,只能在单JVM实例中工做。咱们须要缓存是由于一些对象很“重”,建立它需花费大量的时间和资源。所以咱们维护了对象池用于重用这些对象,而不须要在后面建立。咱们只有当维护缓存比建立对象更廉价时才能得到性能的提升。在集群环境,每一个JVM实例都要维护一份缓存的拷贝,这些拷贝必须同步以维持全部服务器实例状态的一致性。有时这种类型的同步会比没有缓存带来更糟的性能。

8.2.3 Static变量

当咱们设计J2EE应用程序时,在架构上常常会使用一些设计模式。这些如“Singleton”的设计模式会用到静态变量来在多对象之间共享状态。这种方式在单服务中工做得很好,但在集群环境将失效。集群中的每一个实例都会在它的JVM实例中维护一份静态变量的拷贝,这样就破坏了模式的机制。一个使用静态变量的例子就是用它来保持在线用户数。用静态变量来保存在线用户数是一个很简单的办法,当用户进入或离开时就增长和减小它。这种方式在单服务器中绝对是好的,但在集群环境将失效。在集群中更好的方式是将全部状态保存到数据库。

8.2.4 外部资源

尽管J2EE规范不支持,但为各类目的仍然会用外部I/O的操做。例如,一些应用会使用文件系统来保存用户上传的文件,或是建立一个动态配置的 XML文件。在集群环境是没有办法来在其余实例之间来复制这些文件的。为了在集群中工做,办法是用数据库做为外部文件的存放点,另外也可使用SAN(存储区域网,Storage Area Network)做为存放点。

8.2.5 特殊服务

一些特殊的服务只在独立的环境中才有意义,定时服务就一个很好例子,这种服务能够在一个固定的间隔时间有规律的触发。定时服务经常使用于执行一些自动化管理任务。如日志文件滚动,系统数据备份,数据库一致性检查和冗余数据清理等。一些基于事件的服务也很难被迁移到集群环境中。初始化服务就是个好例子,它只在整个系统启动时才发生。邮件通知服务也同样,它在一些警告条件下触发。

这些服务是被事件而不是被请求触发的,并且只被执行一次。这些服务使得负载均衡和失效转移在集群中没多少意义。

一些产品准备了这些服务,如Jboss使用了“集群单例设施”来协调全部实例,保证执行这些服务一次且仅有一次。基于你所选择的平台,一些特殊的服务可能会是把你的应用迁移到集群结构中的障碍。

8.3 分布式结构比并置结构更灵活——不必定

J2EE技术,尤为是EJB,天生就是用来作分布式计算。解耦业务功能,重用远程组件,这些使得多层应用很是流行。可是咱们不能将全部的东西都分布。一些J2EE架构师认为Web层与EJB层并置得越近越好。这些计论后面会继续。先让我解释一下。

 
图 20  分布式结构

如图20所示,这是一个分布式结构。当请求来了,负载均衡器将请求分发到不一样服务器中的不一样WEB容器,若是请求包含了EJB调用,WEB容器将重发EJB调用到不一样的EJB容器。这样,请求将被负载均衡和失效转移两次。

一些人看分布式结构,他们会指出:

  • 第二次负载均衡没有必要,由于它不会使任务分配更平坦。每一个服务器实例都有它们本身的WEB容器和EJB容器。把EJB容器用来处理来自其余实例WEB容器的请求比只在服务器内部调用并无什么优点。
  • 第二次失效转移没有必要,由于它不能是高可用性。多数供应商实现J2EE服务器都会在同一服务器中运行的WEB容器和EJB容器放在一个JVM实例中。若是EJB容器失效了,多数状况下在同一个JVM中的WEB容器也将同时失效。
  • 性能将降低。想像一下对你的应用的一次调用包含一组对EJB的调用,若是你负载均衡了这些EJB,这将跨越每一个服务器实例,致使许多没必要要的服务器到服务器的交互。还有,若是这个方法在事务范围内,那么事务边界将包含许多服务器实例,这将严重影响性能。

实际上在运行期,多数的供应商(包括Sun JES,WebLogic和Jboss)都会优化EJB调用机制,使请求首先选择同一个服务器中的EJB容器。这样,如图21所示,咱们只在第一层(WEB层)作负载均衡,而后调用相同服务器上的服务。这种结构咱们称之为并置结构。技术上说,并置结构是分布式结构的一种特例。

 
图 21  并置结构

一个有趣的问题是,既然多数的部署在运行期都演进成了并置结构,为何不用本地接口代替远程接口,这将大提升性能。你固然能够,可是记住,当你使用本地接口后,WEB组件和EJB耦合得很紧,而方法调用也是直接的而不经过RMI/IIOP。负载均衡和失效转移分发器没有机会介入本地接口调用。 “WEB+EJB”总体处理负载均衡和失效转移。

但不幸的是,在集群中使用本地接口在多数J2EE服务器中有局限性。使用本地接口的EJB是本地对象,是不可序列化的,这一个限制就使本地引用不能保存在HTTP Session中。一些产品,如Sun JES,会将本地接口区别看待,使它们能够序列化。这样就能够用在HTTP Session中。

另外一个有趣的问题是,既然并置结构这么流行而且有好的性能,为何还要分布式结构呢?这在多数状况下是有道理的,但有时分布式结构是不可替代的。

  • EJB不只被WEB容器使用,富客户端也会使用它。
  • EJB组件和WEB组件需在不一样的安全级别上,并须要物理分离。这样防火墙将被设置用于保护运行EJB的重要机器。
  • WEB层和EJB层极端不对称使得分布式结构是更好的选择。好比,一些EJB组件很是复杂而且很消耗资源,它们只能运行在昂贵的大型服务器上,另外一方面,WEB组件(HTML,JSP和Servlet)简单得只需廉价的PC服务器就能知足要求。在这种状况下,专门的WEB服务器能够用来接受客户端链接请求,很快处理静态数据(HTML和图像)和简单的WEB组件(JSP和Servlet)。大型服务器只被用来作复杂计算。这将更好的利用投资。

9 结论 集群与独立环境不一样,J2EE供应商采用不一样的方法来实现集群。若是你的项目为作到高伸缩性而使用集群,你应该在你的项目开始的时候就作准备。选择符合你的需求的正确的J2EE产品。选择正确的第三方软件和框架并确保它们能支持集群。最后设计正确的架构使得能从集群中受益而不是受害。

相关文章
相关标签/搜索