JEE Web集群

sincosin

贡献于2011-06-02

字数:0 关键词: 集群 应用服务器

免费在线版本 (非印刷免费在线版) 了解本书更多信息请登录本书的官方网站 InfoQ 中文站出品 本书由 InfoQ 中文站免费发放,如果您从其他渠道获取本书,请注册 InfoQ 中文站以支持 作者和出版商,并免费下载更多 InfoQ 企业软件开发系列图书。 本迷你书主页为 http://www.infoq.com/cn/minibooks/jee-webserver-cluster 1 目录 1 Tomcat集群代码分析 .................................................................................................................... 3 1.1 Session .................................................................................................................................. 4 1.2 Session Manager .............................................................................................................. 4 1.3 组通讯框架--Tribe ............................................................................................................. 5 1.4 Cluster ................................................................................................................................... 6 2 Geronimo Web层集群分析 ........................................................................................................ 7 2.1 WADI代码分析 ................................................................................................................... 7 2.2 WADI中的相关概念 ........................................................................................................ 11 2.3 Geronimo如何集成WADI & Session复制 ............................................................... 12 2.4 Geronimo Session复制过程 ........................................................................................ 16 3 GlassFishV2 中的WEB层集群 ................................................................................................... 19 3.1 GlassFish的Session复制模式 ...................................................................................... 19 3.2 Shoal集群框架 ................................................................................................................. 19 3.3 GlassFish如何集成Shoal ................................................................................................ 24 4 JOnAS中的WEB层集群 .............................................................................................................. 27 4.1 简介 ................................................................................................................................... 27 4.2 Domain管理架构 ............................................................................................................ 29 4.3 WEB集群配置 ................................................................................................................... 32 4.4 WEB层集群部分代码研究 .............................................................................................. 32 5 JBoss中的WEB层集群 ................................................................................................................. 35 5.1 集群代码分析 ................................................................................................................... 35 5.2 JBoss Cache介绍 ............................................................................................................ 35 5.3 JBoss Cache实战 ............................................................................................................ 37 6 测试数据分析 ............................................................................................................................... 39 6.1 理论分析结果 ................................................................................................................... 39 6.2 实际测试数据结果 .......................................................................................................... 40 6.3 测试过程中所发现的一些问题 ..................................................................................... 42 7 结论与建议 .................................................................................................................................... 46 2 深入理解各 JEE 服务器 Web 层集群原理 (张华,veryhua2006@163.com) JEE 服务器集群可细分为 Web 层集群、EJB 集群、JMS 集群等等。Web 层集群主要包括前端 的负载均衡及 Session 复制,本文主要关注 Web 层集群方面的 Session 复制。 针对不同的 JEE 服务器,如 Tomcat、Geronimo、GlassFish V2、JOnAS、JBoss,我首先从网络 上的各种公开资料做了理论分析,然后走读了相关的源代码,最后做了一些测试,并且根据 Session 结构、逻辑结构、组播框架、复制策略、通用性等五个方面给出了对比表。测试数 据结果与理论分析、代码分析的结果完全一致。 本文适合下列场景的人员阅读:  有一定技术功底的读者  希望深入了解各 JEE 服务器 WEB 层集群原理的读者  希望对 JEE 服务器进行选型的读者  希望寻找某个独立的 WEB 层集群框架用于集成到某个 JEE 服务器中的读者  WEB 层集群出现了某些问题,最后只能从理论和代码分析中找到答案的读者 深入理解各 JEE 服务器 Web 层集群原理 3 1 Tomcat集群代码分析 Tomcat 是一款非常优秀的 Java Web 服务器,以致于很多开源 Java 应用服务器(如 JOnAS) 直接集成它作为 servlet 容器。在你阅读完本文全篇后,你会体会到各 JEE 服务器的 Web 层 集群的原理及很多概念都能在 Tomcat 身上找到对应物。所以为了便于比较,本文将首先阐 述 Tomcat 集群是如何进行 Session 复制的,至于具体如何配置一个 Tomcat + Apache 集群, 读者可在网上自行搜索相关资料。 下图 1-1 为 Tomcat 集群源码的类图: ManagerBase Manager ClusterManager ClusterManagerBase PersistentManagerBase StandardManager DeltaManagerPersistentManger BackupManager SimpleTcpReplicationManager Cluster CatalinaCluster SimpleTcpCluster org.apache.catalina.tribes Session ClusterSession StandardSession DeltaSession ReplicatedSession 图 1-1 tomcat 集群源码类图 从图中我们可以看出 Tomcat 集群包括以下几个方面的内容:  Session: Session 分为 StandardSession 与 ClusterSession 两种,后者用于 Session 复制。  Session Manager: 有用于集群 Session 管理的 ClusterSession,也有用于对 Session 进行一 般日常管理的,如 PersistentManager, BackupManager, SimpleTcpReplicationManager。 深入理解各 JEE 服务器 Web 层集群原理 4  组通迅框架:Session Manager 调用组通讯框架进行 Session 的传输,Tomcat 采用的组通 讯框架是 tribe,目前 tribe 已被独立为开放的 apache 工程。  Cluster: 方便集群管理而派生出的逻辑概念,可将实际物理机划分为一个 Cluster,也可 将一台物理机上不同端口的实例划分为一个 Cluster,它有一个简单的实现类 SimpleTcpCluster。 1.1 Session 服务器集群通常操纵两种 session:  Sticky sessions: 尽量让同一个客户请求由同一台服务器来处理,这样 sticky sessions 就是 存在于单机服务器中接受客户端请求的 session,它不需要进行 Session 复制,如果这个 单机失败的话,用户必须重新登录网站。  Replicated sessions: 在一台服务器中的 session 状态被复制到集群的其他服务器上,无论 何时,只要 session 改变了,session 数据都要重新全部或部分(依据复制策略)被复制 到其他服务器上。 Tomcat 支持以下三种 session 持久性类型:  内存复制:在 JVM 内存中复制 session 状态,使用 Tomcat 自带的 SimpleTcpCluster 和 SimpleTcpClusterManager 类。  数据库持久性:在这种类型中,session 状态保存在一个关系数据库中,服务器使用 org.apache.catalina.session.JDBCManager 类从数据库中获取 SESSION 信息。  基于文件的持久性:这里使用类 org.apache.catalina.session.FileManager 把 session 状态 保存到一个文件系统。 1.2 Session Manager Tomcat 通过 org.apache.catalina.Manager 来管理 Session,Manager 接口总是和 Context Container 相关联。它主要负责 session 的建立、更新和销毁。该接口中一些重要的方法有: public Session findSession(String id) throws IOException; public Session createSession(String sessionId); public void load() throws ClassNotFoundException, IOException; public void remove(Session session); public void setContainer(Container container); public boolean getDistributable(); 1 Tomcat 集群代码分析 5 用户在 Servlet 中通过 javax.servlet.http.HttpServletRequest 接口的 getSession 方法获得 Session,而该接口的实现位于 org.apache.catalina.connector.Request 类中的 doGetSession 方 法中,在该方法中通过 org.apache.catalina.Manager 来获得 Session , doGetSession 方法的 部分代码如下: 1.3 组通讯框架 --Tribe 组通讯框架 Tribe 在 Tomcat 中的位置可如下图 1-2 所示: 图 1-2 Tomcat 与组通讯框架 Tribe 的衍接图 如图所示,Tribe 的核心主要是 Channel 类,由此看出,它采用 NIO 进行 Socket 通讯,运用 了组播,事件、心跳检测等技术,下面我们来着重看看代码中 Tomcat 是如何与 Tribe 衍接的: 深入理解各 JEE 服务器 Web 层集群原理 6 首先在 SimpleTcpReplication 类中的实现 Manager 接口的 start 方法中: 它调用了 SimpleTcpCluster 类的中 send 方法发送消息,该方法真正调用 tribes 组通讯框架发 送消息,该方法如下: 1.4 Cluster Cluster 用于管理集群中的 Session 复制,它有一个简单的实现类 SimpleTcpCluster。 深入理解各 JEE 服务器 Web 层集群原理 7 2 Geronimo Web层集群分析 Geronimo 采用 WADI 来支持 WEB 层集群,WADI 已发展为一个独立通用的集群框架,也可 以单独用在其他地方。 2.1 WADI代码分析 WADI 的包结构图如下图 2-1 所示: org.codehau s.wadi.aop org.codehau s.wadi.core org.codehaus .wadi.group org.codehau s.wadi.tribes org.codehaus. wadi.jgroup org.codehaus. wadi.cache org.codehaus.wadi .cache.openjpa 图 2-1 WADI 的包结构图  org.codehaus.wadi.core : 提供了 Cluster、ClusterManager、Session 等核心类。  org.codehaus.wadi.group, org.codehaus.wadi.tribes, org.codehaus.wa.di.jgroup : 组通讯 框架  org.codehaus.wadi.aop : 提供 AOP 功能  org.codehaus.cache : 提供分布式缓存 2.1.1 org.codehaus.wadi.core 深入理解各 JEE 服务器 Web 层集群原理 8 图 2-2 org.codehaus.wadi.core 包类图  和 tomcat 一样,类 WADIHttpSession 的 getWrapper 方法相当于 facade,它可以获得 org.apache.catalina.Session  和 Tomcat 一样,它同样有 SessionManager, Cluster 的等同物,也有拓扑管理器 (SimplePartionManager)  网络通讯交由 org.codehaus.wadi.group 包去完成,而 Tomcat 使用 tribes 组通讯框架, org.codehaus.wadi.group 相当于在 tribes 的基础上又封装了一层  BackingStrategy 接口的实现类只有一个 RoundRobinBackingStrategy, 说明目前 WADI 只 支持 RoundRobin 一个策略 2 Geronimo Web 层集群分析 9 2.1.2 org.codehaus.wadi.group 图 2-3 org.codehaus.wadi.group 包类图  Cluster 在 Tomcat 中有相同的等价物。  Dispatcher、Address、Peer 是新添加的抽象。  Envelope 用于结对复制,将在下面说明。 深入理解各 JEE 服务器 Web 层集群原理 10 2.1.3 org.codehaus.wadi.aop 图 2-4 org.codehaus.wadi.aop 包类图  使用了访问者及备忘录设计模式  使用了 aspectJ 这个 AOP 实现。  如果一个对象要复制,添加@ClusteredState 注解即可。例如: 2 Geronimo Web 层集群分析 11 2.2 WADI中的相关概念 2.2.1 Group Communication Services & Service Spaces 图 2-5 Group Communication Services & Service Spaces 结构 一个结点有一个通过 Dispatcher 与 Cluster 抽象的组通讯组件,各组通讯组件之间通过 Envelope 通信。WADI 提供了四种组通讯框架的实现,分别是:内存、Tribes、JGroups 与 ActiveCluster。 Service Space 是构建在组通讯组件上的一层抽象,组通讯组件提供实际物理组通讯服务,而 它提供逻辑上的组通讯服务。也就是说,只有在同一个 Service Space 内的结点才能通信 (Session 复制)。例如,在 Service Space 1 中只有 node1 与 node3,那么在 node1 中的组通 讯 client api 是只能看得到 node3 的。在 Service Space 里可以有 Peer。 Partition 是指各 Peer 之间的连线,我把它翻译成拓扑(也可以理解成对其他结点的 Session 复 制),这些 Partition 说明各集群成员间共享。 深入理解各 JEE 服务器 Web 层集群原理 12 2.2.2 Envelope在取模复制中的应用 在Tomcat中,复制策略采用全复制,即一台Tomcat服务器的Session发生改变,将广播到集群 内其他所有的Tomcat服务器中。当集群规模很大时,大量的广播容易造成网络阻塞。在我们 实测经验中,这个临界值时是 6。 在Glassfih中,复制策略采用结对复制, 即一台Glassfih服务器的Session发生改变,只广播到 集群内邻近的下一台Glassfih服务器中。 在Geronimo中,复制策略则采用取模复制。具体取模复制策略的算法是: Math.abs(key.hashCode()%_numPartitions) 其中,_numPartitions 指 Partition 的个数。 这样我们 可以调整取模算法的相关参数控制一台Geronimo服务器上的Session变化广播到我 们所想要的任何Geronimo服务器。例如 在var/config/config-substitutions.properties文件中, 可令DefaultWadiNumPartitions等于集群结点数目(默认值是 24),该值即是取模复制策略算 法中的_numPartitions; 令ReplicaCount 它等于 1(默认值是 2),该值即是一个Session被复制的次数。 更多介绍可参见资料:http://docs.codehaus.org/pages/viewpage.action?pageId=9764992 2.3 Geronimo如何集成 WADI & Session复制 WADI 作为一个独立通用的集群框架,在 Geronimo 中,集成它的部分类图如 2-6 所示: 2 Geronimo Web 层集群分析 13 图 2-6 Geronimo 集成 WADI 从上述类图中,我们可以看到 Session 复制的流程如下: 1. 要配置 WADI 集成,依赖于下面两个配置:  org.apache.geronimo.configs/tomcat6-clustering-wadi//car:它定义了运行时依赖性和 Tomcat6 集群合同。  org.apache.geronimo.configs/tomcat6-clustering-builder-wadi//car:它定义了部署时依 赖性以及声明缺省集群配置的 TomcatClusteringBuilder GBean。 所以, org.apache.geronimo.configs/tomcat6-clustering-builder-wadi//car 配置的 plan.xml 如下: 深入理解各 JEE 服务器 Web 层集群原理 14 我们可以看到,该 plan.xml 配置了一个 GBean ,这个 GBean 类是: org.apache.geronimo.tomcat.cluster.wadi.builder.WADITomcatClusteringBuilder 2. 在 WADITomcatClusteringBuilder 这个 GBean 中监控了 BasicWADISessionManager 这个类, 其相关部分如下图: 3. BasicWADISessionManager 类的相关方法如下。这个类的 creatSession 方法委托 WADI 的 Session 管理器来创建 Session,同时在 doStart 方法中启动了 WADI 中的 ServiceSpace。 2 Geronimo Web 层集群分析 15 4. 在上述的 AOPStackContext 类中我们继续跟踪 numPartitions 这个参数。在 AOPStackContext 的父类 StackContext 类( 位 于 WADI 的 aop 包中)的 newPartitionMapper 方法使用了这个参数。 5. SimplePartitionMapper 类如下: 深入理解各 JEE 服务器 Web 层集群原理 16 这个类便是为 WADI 提供 Session 复制策略的类,可以看出,它既不是像 Tomcat 一样的 全部复制,也不是像 GlassFish 一样的结对复制,它是一种新型的取模复制。 上述的 key 代表什么呢,SimpleStateManager 类有如下片断: 现在我们明白了,原来 key 就是 Motable 对象的一个 ID 属性,而 Session 实现了 Motable 接 口,所以说,这个 key 就是 Session 的 ID。而 Session 的 ID 是随机变化的,根据算法 (Math.abs(key.hashCode()%_numPartitions)),可以知道一个结点上的 Session 可以被复制到 任何结点上去(这点不同于结对复制),我们可以通过调整 numPartitions 参数来决定 Session 被复制的个数。 2.4 Geronimo Session复制过程 http://docs.codehaus.org/display/WADI/4.+Session+Replication 1. 首先需要设定 partitionNum(位于 var/conf/ config-substitutions.properties 文件的 DefaultWadiNumPartitions 参数) 2 Geronimo Web 层集群分析 17 比如说设定的 partitionNum=12 的话:  有一个结点机器 node1 时,node1 上就会有 12 个 Partition  有二个结点机器时,node1 与 node2 上各 6 个 Partition  有三个结点机器时,node1、node2、node3 上各 4 个 Partition 各 Partition 会有一个 index,从 0 开始,如下图: 2. 一用户请求刚好被负载均衡器访问到 node1 机器上,根据 Session 复制策略算法 (Math.abs(key.hashCode()%_numPartitions)可以决定 Session 被分配到哪个 partition 上去 (取模之后,结果值一定会在 Partition 的 index 范围之内)。当然,这个 Session 复制策略 算法是可以被替换的。 3. 比如说结点 node3 损坏之后,node1 会重新根据复制策略决定在 node4 建立新副本。 深入理解各 JEE 服务器 Web 层集群原理 18 深入理解各 JEE 服务器 Web 层集群原理 19 3 GlassFishV2 中的WEB层集群 3.1 GlassFish的Session复制模式 GlassFish V2 支持两种 Session 复制模式:  集中式:采用 HADB  内存复制:采用结对复制 本文主要讨论的是内存复制。 3.2 Shoal集群框架 GlassFishV2 集群采用了 Shoal 集群框架。 3.2.1 Shoal简介 作为从 GlassFish 中剥离出来的子项目,Shoal 很好的实现了集群中节点统一管理和共享状态 数据这两个重要功能,并对集群之中各个节点的加入、关闭、失败等状态实施监控和及时的 消息通知。Shoal 使用 JXTA 协议来提供 peer-to-peer 网络计算的可靠性和扩展性,在集群中 的节点均可以根据需要加入或退出集群,并且节点可以收到其他节点的消息。 Shoal 提供了 Group Management Service (GMS),GMS 通过 join, shutdown, failure notifications, delegated recovery initiation 等事件及 state caching facilities 来维护组成员关系, 应用通过 GMS API 来和各组成员之间通信。 深入理解各 JEE 服务器 Web 层集群原理 20 图 3-1 Application, Shoal GMS, Group Communication Provider 之关的关系 Shoal 是一个基于 java 的动态集群框架,为构建容错、可靠和实用的 Java EE 应用服务器提供 了基础架构支持。Shoal 是 GlassFish 与 JonAS 应用服务器的集群引擎,它还可以插入到需要 集群和分布式系统支持的任何 java 应用中。Shoal 的集群方式来自于一个容错的基础架构。 它提供了一个组事件通知模型使得消费应用程序(consuming applications)可以解决分布式 系统领域中的包括数据复制在内的很多难题。 可以使用Shoal API来对web应用,集群EJB和JMS组件中的HTTP session进行复制。GlassFish应 用服务器中的EJB容器、事务服务、定时服务、Orb、Session复制模块以及其他组件都使用了 Shoal来与集群中的成员进行交互。用户可以将Shoal用于集群几乎任何产品:JMS(MQ 3 GlassFishV2 中的 WEB 层集群 21 clusters)、数据库(指 Postgres或者 MySQL集群),甚至进行 CVS和 Subversion的集群。 (这主 要依赖于Shoal的分布式缓存功能) Shoal框架还有一个叫做分布式状态缓存(Distributed State Cache,DSC)的共享分布式存储 特性,它可以用来在内存中对应用的状态进行分布式缓存。GMS为轻量级复制缓存提供了一 个默认实现。 Sreedhar将DSC与其他的java对象缓存框架如 JBossCache、OSCache和 EHCache 进行了对比。Shoal中的分布式状态缓存是一个接口,针对该接口可以有多种实现。默认实 现是一个简单的共享缓存,可以处理轻量级消息传递如配置数据、组状态机等等。 它不适 合进行高吞吐量的缓存,也不支持基于LRU(Latest Recently Used)的缓存校验与分布式的锁 语义。 Shoal的另一个使用地方是作为一个计算网格应用的底层引擎。FishFarm项目当前正使用 Shoal做这个事情。 Shoal内部实现采用了组通信框架JXTA。JXTA不需要指定单独的TCP和UDP传输就可以进行通 信,并且它还不限于IP(支持RF,BT等)。当在集群中进行广播时,JXTA可以动态决定向集 群成员发送消息的最佳方式,这是通过IP广播和虚拟广播来实现的。如果遵循服务提供者 API,我们可以换成其他的实现,因此可以采用JGroups、基于 Appia或者基于 JINI的组通信提 供者。 Shoal框架提供了客户端APIs以发出如运行时集群成员的增加或减少这样的事件。一个成员可 以以核心成员(其失败会被通知给集群中所有其他的成员)或旁观者成员(其失败不会被通 知给集群中其他成员,但它会收到其他核心成员的所有通知)的方式加入到组中。Shoal的 核心服务是组管理服务(Group Management Service——GMS),它给客户端(JVMs)提供了 一个组消息句柄,使其可以向组或集群中特定的成员发送消息。Shoal还提供了其他一些非 常棒的特性,例如面向恢复的信号及支持,自动恢复成员选择(Shoal自动委托恢复初始化 ) 和失败保护操作。 GlassFish 需要能满足若干个 GF 组件的集群解决方案,例如 IIOP 负载平衡,Session 复制模块, 事务服务模块等等。 在 IIOP 负载平衡的情况下,需求如下:当一个失败产生时,orb 应该让其远程客户端对集群 中的其他成员也产生失败的结果。连接到特定实例的 orb 的远程客户端会通过 Shoal 的事件 通知机制得到动态的集群变化信息以及 IIOP 端点地址。 在事务服务模块的情况下,需求如下:根据一个失败成员的事务日志,从一个远程集群成员 中执行自动的事务恢复操作以使得在失败时刻未完成的事务得以完成。为了支持上述特性, Glassfish提供了一个恢复服务器选择通知 (recovery server selection notification)以及失败防 深入理解各 JEE 服务器 Web 层集群原理 22 护支持。 3.2.2 使用Shoal编程 组通讯代码片断: 1. 首先要启动一个 GMSModule GroupManagementService gms = (GroupManagementService) GMSFactory.startGMSModule(serverName, groupName, memberType, props); groupName : 组名,组名不存在就 create,组名已存在就 join serverName : 名字,在一个集群内,这个名称不能重复 memberType : 枚举类型,CORE or SPECTATOR props : 配置参数,如 JXTA 网络的配置参数,超时,重试次数等等. 2. 在一个组创建成功后,必须调用 join 方法: gms.join(); 3. 当成了一个组的组员之后,就可以发消息了 gms.getGroupHandle().sendMessage(null, message.getBytes()); 以上这一句发送消息给所有的组成员,第一个参数是一个组件名称,为 null 就代表所有 组成员。 4. 监听组的消息,要实现一个 CallBack 接口,现里面的 processNotification()方法,如下: 要用以上代码的话,还必须先登记: 如果没有采用上述的用消息的方式在各节点之间共享数据的话,还可以采用分布式缓存(DSC) 来共享数据。 3 GlassFishV2 中的 WEB 层集群 23 //获取DSC引用 DistributedStateCache dsc = gms.getGroupHandle() .getDistributedStateCache(); //往缓存加数据 dsc.addToCache( componentName, memberTokenId, key, state); //从缓存查询数据 Object o = dsc.getFromCache( componentName, memberTokenId, key) ; //从缓存移除数据 dsc.removeFromCache( componentName, memberTokenId, key) ; 完整例子如下: 深入理解各 JEE 服务器 Web 层集群原理 24 3.3 GlassFish如何集成Shoal GMS 管理 GlassFish 中的群集形状变化事件,并根据成员加入、成员正常关闭或成员发生故 障等事件进行相应的调整。内存复制通过 GMS 执行必要的操作以响应这些事件,并提供连 续的服务可用性。在 GlassFish 应用服务器中使用 GMS 来监视群集的运行状况并支持内存复 制模块。简而言之,GMS 提供以下支持:  群集成员身份变化通知和群集状态  整个群集范围内或成员之间的消息传送  面向恢复的计算,其中包括恢复成员选择、故障防护以及针对多个故障的恢复链  分布式高速缓存,这是一种适于交换有关群集成员身份消息的轻型实现  用于接入组通信提供者的服务提供者接口 (Service-provider Interface, SPI);缺省提供 者基于 JXTA 技术  计时器迁移 – 如果需要的话,GMS 会选择一个实例来接收故障实例的计时器 在GlassFish 版本V2 应用服务器中,内存复制功能基于JXTA技术的传输和消息传送功能。许 多人都把JXTA技术视为对等技术。它被定义为一组基于XML的协议,连接到网络上的设备可 通过这些协议交换消息并协同工作,而不会受到网络拓扑的限制。在开发 GlassFish 版本V2 应用服务器时,改进了JXTA技术以满足内存复制的高容量和吞吐量需求。为了提高可伸缩性 和性能,内存复制功能开发者还与 Grizzly项目 进行了有益的协作,此项目旨在帮助开发者 使用 Java New I/O API (NIO) 构建可伸缩的可靠服务器。 JXTA 技术中的组成员关系概念可以很好地映射到 GlassFish 应用服务器群集和实例模型:JXTA 组映射到 GlassFish 群集,JXTA 对等项映射到 GlassFish 服务器实例。GMS 充分利用这些组成 员关系概念,提供了一些消耗性组件,如内存复制(一种用于处理群集中的运行时事件的通 知事件模型)。 3 GlassFishV2 中的 WEB 层集群 25 图 3-2 Glassfish 中集成 Shoal 在 Glassfish 中,没有 Session 及 SessionManager 相关的类,这些类延用了 Tomcat 的类。而 在 WADI 中,为了通用性,WADI 将这些类全抽象出来了。就从这点看,GlassFish 的性能应 该比 Geronimo 好。 GlassFish 采用 Shoal 做为通讯框架,而 Shoal 采用 JXTA 实现组通讯功能,JXTA 虽然是一个 P2P 框架,但它在这里的作用和组通讯框架如 Tribes、JGroup 类似。 GlassFish 实现了 Tomcat 的一个 Value (HASessionStoreValve) GlassFish 中调用 Shoal 的入口代码可见 JxtaReplicationReceiver 类的 registerWithGMS 类,如 下: 深入理解各 JEE 服务器 Web 层集群原理 26 深入理解各 JEE 服务器 Web 层集群原理 27 4 JOnAS中的WEB层集群 4.1 简介 图 4-1 JonAS 集群架构图 从上图可以看出,JOnAS 在 web 层集群的方案和 Tomcat 是一模一样的:  负载均衡:采用 mod_jk 的方式  Session 复制:采用 Tomcat 自身的方式 在JOnAS中有一个Replication domain的概念,官网的说法“A JOnAS domain gathers a set of JOnAS nodes under the same administration authority. A domain is defined by a name and the JOnAS nodes belonging to a domain must have an unique name within this domain.”,如下图所 示,也就是在一个domain里的node才能相互复制的。 深入理解各 JEE 服务器 Web 层集群原理 28 图 4-2 JOnAS 中的 Replication domain EJB farming 依赖于 CMI,CMI 是一系列 RMI 协议(jrmp,iiop,irmi)上层的一个抽象。当 Client 要访问 EJB 时,它先要调用 JNDI registry 查询到 EJB home or EJB remote proxy, 所以 JNDI registry 必须得复制到各个结点上。各个结点间的通讯采用 JGroup 框架。 另外,CMI 不仅提供了一个抽象,还提供了一个负载均衡,它实现了一系列算法,如 rond-robin,first available, random 等。 4 JOnAS 中的 WEB 层集群 29 图 4-3 JonAS 中的 CMI 架构 4.2 Domain管理架构 每个结点都可以通过本地的 jonasAdmin 管理控制台管理,而 domain 管理能够通过一个中心 jonasAdmin 管理控制台远程管理一系列结点。 域管理具有如下特性:  发现服务:自动检测到 instances and clusters  监测服务:能够监测到 instances and clusters 的状态  控制服务:能够远程控制 instances and clusters  部署服务:在 domain-wide 自动,如只需要在中央结点一下,其他 domain-wide 范围内 的结点自动都部署了  配置服务:设置参数 有三种手段实现上述服务:  jonasAdmin 控制台  命令行  JMX 深入理解各 JEE 服务器 Web 层集群原理 30 Domain.xml 文件示例如下: 4 JOnAS 中的 WEB 层集群 31 Clusterd.xml 文件示例如下: 深入理解各 JEE 服务器 Web 层集群原理 32 命令行启动域的命令是:jonas start -n masterName -Ddomain.name=domainName 4.3 WEB集群配置 JOnAS的Session复制是完全采用的Tomcat的,所以WEB集群配置与Tomcat的配置方法一模一 样,略。可参见文档:Building the JOnAS's cluster configuration。 4.4 WEB层集群部分代码研究 4.4.1 JOnAS如何集成Tomcat 在JOnAS中,可以定义Service,就是一个实现了 org.objectweb.jonas.service.Service接口的类, 该接口定义如下: 在\JOnAS\jonas\jonas-full-5.1.0\conf\ jonas.properties 文件中配置了一个名为 web 的实现类 org.ow2.jonas.web.tomcat6.Tomcat6Service: 紧接着下面一段在 jonas.services 中配置了这个”web”,也就是说,JOnAS 一启动,这个名为 web 的 Service 就会启动。 4 JOnAS 中的 WEB 层集群 33 在 org.ow2.jonas.web.tomcat6.Tomca6Service 的 doStart 方法中直接启动了 Tomcat 在 startInternalWebContainer 方法中直接通过 Digest 这个 XML 解析器解析 Tomcat 自身的配 置文件(server.xml)构建了 Tomcat 组件,从而启动了 Tomcat, startInternalWebContainer 方法中的部分代码片断如下: 深入理解各 JEE 服务器 Web 层集群原理 34 4.4.2 JOnAS中的Session复制代码研究 从上节中,我们知道了 JOnAS 集成 Tomcat 的方式,我们也就知道了 Session 复制之类的工作 实际上完全是 Tomcat 自身的代码实现的,略。 深入理解各 JEE 服务器 Web 层集群原理 35 5 JBoss中的WEB层集群 5.1 集群代码分析 图 5-1 Jboss 集群结构图 从上图 5-1 我们知道,JBoss 是采用分布式缓存(JBoss Cache)来实现集群的。 5.2 JBoss Cache介绍 从持续存储、尤其是数据库中读取数据需要付出昂贵的代价。而且,数据库在伸缩性方面也 是臭名昭著(或者不便宜),当你想要扩展前端或增加更多客户端时,这个弊端显然就成了 障碍。另一方面,CPU 和内存的价格越来越便宜,这意味着更多的人可以负担得起架设高可 用系统所需的成本。“本站正在维护中”的暂停服务方式都应当成为历史。 像 JBoss Cache 这样的分布式缓存扮演的是一个处于应用服务前端和数据库间的中间层的角 色,提供对持久性数据状态在内存中的快速访问。JBoss Cache 能够确保缓存中的数据状态 和数据库中的状态一致、及时更新数据状态、并且保证 JVM 不会出现堆溢出问题。 一些开源项目用到了 JBoss Cache。Hibernate(以及 JBoss Application Server 的 EJB3 实现)使 用 JBoss Cache 来存储从数据库后端读取的实体数据,这样一来在调用实体时就不需要每次 深入理解各 JEE 服务器 Web 层集群原理 36 都连接到数据库去查找。我这样说只是一个简单的概 括,Hibernate 运用分布式缓存的实际 操作其实更复杂。Seam 也通过分布式缓存来缓存生成 JSF 页面元素,从而改善那些页面或 者页面元素生成速度比较缓慢的站点的伸缩性。另外还有一些开源项目,如 Lucene、 Hibernate Search、GridGain、JBoss 应用服务器的 HTTP Session 集群和集群的单点登录(Single Sign-On)代码等都用到了 JBoss Cache。 JBoss Cache 提供两种缓存方式:核心缓存和 POJO 缓存:  核心缓存会直接把你传递给它的数据存储在一个树型结构中。键/值对被存储在树的节 点上,出于复制或持续性的需要它们都被序列化了。  POJO 缓存则采用比较复杂的机制——利用字节码编织来内省(introspecting)用户类, 并向用户类的域添加侦听器,一旦域值有任何变化,侦听器会立刻通知缓存。例如,如 果要在 POJO 缓存中存储一个庞大、复杂的对象,会导致 POJO 缓存内省对象的字节码, 最终只把该对象的原始域存储到树结构中。一旦域值有所变化,缓存只复制这个改变了 的域值而不会去复制整个用户类,这是高效的细粒度复制。 对于用户来说,为什么要选择本地缓存,而不用 HashMap 呢?很多人认为 Map 是考虑缓存 的出发点(实际上,JSR-107 JCACHE 专家组曾经在 Map 的基础上扩展实现 javax.cache.Cache)。 尽管 Map 非常适合用来存储简单的键/值对,在缓存必需的其它特性上,它就难免有点黔 驴技穷。比如内存管理(eviction)、钝化(passivation)和持续性、细粒度锁定模型(首先, HashMap 根本不是线程安全的;而 ConcurrentHashMap 采用的锁是粗粒度级的,它甚至不允 许非阻塞用户或多用户从 map 中读取数据)等。而对于“合格的”缓 存来说,它还需要具备 一些“企业”特性,包括 JTA 兼容、附加侦听器等功能。Map 虽然是个好的起点,但如果需要 实现或者管理我刚才提到的那些特性的话,选择缓存还是要比 Map 来得更合适一些。 JBoss Cache采用传统的悲观锁(pessimistic locking)的方式 ,树结构中的每个节点对应一个 锁。这些锁的隔离级别和数据库实施的隔离级别相同,允许多用户同时读取数据。JBoss Cache 也提供乐观锁定(optimistically lock)方式,这个方式则牵涉到数据版本、每个事务的副本 维护、主要树结构提交的事务副本确认等等。在乐观锁定方式下,需要承载大量的数据读取 请求的系统因此可以获得高度并发性。那些请求读取数据的用户不会因为并发数据库写入操 作而受到阻塞。而且,乐观锁定方式还可以避免悲观锁定中有可能发生的死锁。JBoss Cache 3.0.0 携带 多版本并发控制(Multi Versioned Concurrency Control--MVCC)功能。大部分数 据库系统都用到了多版本并发控制这种锁定方式,它为我们提供了最好的乐观锁定和悲观 锁。由于JBoss Cache的实现不会阻碍任何用户读取数据,因此在数据访问速度上较之前者也 胜出百倍。在MVCC功能相对稳定之后,JBoss希望能把它设置为JBoss Cache默认的锁定机制。 5 JBoss 中的 WEB 层集群 37 JBoss Cache 用 JGroups 作为组通信类库,用来侦测组成员和组建集群。我们也把 JGroups 作 为一个信道,在其上我们实现了一个 RPC 机制与组中其它缓存进行通讯。由于 JGoups 的应 用,JBoss Cache 获得了高度灵活性,并在网络协议和调整方面也极具扩展性。JBoss Cache 因此还使得缓存能够摆脱 LAN 集群的框框,能够穿透防火墙的限制并组建 WAN 集群等。 目前,JBoss Cache 支持两种方式——全局复 制( total replication——TR)和 buddy 复 制( buddy replication——BR)。全局复制将状态复制给小组中的所有成员。这种方式能够帮助成员间共 享数据状态,保证在失败转移时可以转移到小组中的任何一个成员,但它限制了系统的伸缩 性。Buddy 复制则挑选特定成员担当备份数据的责任,数据状态相应地只会复制到这些特定 节点上。也就是说直接转移到复制节点的失败转移效率非常高,但即使转移到任何非复制节 点,失败转移也同样都顺利进行,因为数据状态会根据请求转移到相应的节点。BR 最好用 于 session 密切相关(session affinity)的情况下,因为数据状态的代价可能很高,所以应该 尽量仅仅在发生失败转移的时候调用它。 5.3 JBoss Cache实战 直接贴代码吧,如下: package test; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.parsing.XmlConfigurationParser; /** * @author 张华 */ public class JBossCacheTest { public static Cache createCache(String clusterName) { // X parser = new X(); // Configuration conf = parser.px(JBossCacheTest.class.getResourceAsStream("jboss-cache-configs.xml ")); // conf.setClusterName(clusterName); // Cache cache = DefaultCacheFactory.getInstance().createCache(conf , true); Cache cache = DefaultCacheFactory.getInstance().createCache(); return cache; } public static void main(String[] args) throws InterruptedException { int count = 10000; Map caches = new HashMap(); Cache cache = null; 深入理解各 JEE 服务器 Web 层集群原理 38 // 初始化i个cache for (int i = 0; i < 1; i++) { cache = JBossCacheTest.createCache("test"); caches.put("" + i, cache); } System.out.println("--init ok--"); Thread.sleep(1000); long t = System.currentTimeMillis(); System.out.println("--start init date--"); for (int i = 0; i < count; i++) { cache.put(new Fqn("/1235"), "key" + i, "value" + i); } System.out.println("init coust time:" + (System.currentTimeMillis() - t)); t = System.currentTimeMillis(); System.out.println("--start get date--"); for (int i = 0; i < count; i++) { cache.get(new Fqn("/1235"), "key" + i); System.out.println(cache.get(new Fqn("/1235"), "key" + i)); } System.out.println("coust time:" + (System.currentTimeMillis() - t)); t = System.currentTimeMillis(); for (int i = 0; i < count; i++) { cache.put(new Fqn("/1235"), "key0", "value" + i); } System.out.println("update coust time:" + (System.currentTimeMillis() - t)); Thread.sleep(50000); } } class X extends XmlConfigurationParser { public Configuration px(InputStream s) { return this.parseStream(s); } } 深入理解各 JEE 服务器 Web 层集群原理 39 6 测试数据分析 6.1 理论分析结果 下表是我根据 Session 结构、逻辑结构、组播框架、复制策略、通用性等五个方面对 Tomcat6、 Geronimo、Glassfish V2、JBoss、JOnAS 所作的理论对比。 Tomcat6 WADI/Geronim o GlassFishV 2 JBoss JOnAS Session Session 多层抽象 Tomcat Tomca t Tomcat 逻辑结构 SimpleTcpClust er Group Domain Tomca t SimpleDoma in 组播框架 Tribes Tribes/JGroup Shoal/JXTA Cache Tomcat 复制策略 两两复制 取模复制 结对复制 Cache Tomcat 通用性 通用 表 6-1 集群理论分析对比表 1. JOnAS 集群的 Session 部分完全是基于 Tomcat 的,它同样是两两复制。在此基础上,有 了简单域管理的概念。 2. WADI 的目标是想做成一个通用的集群框架,所以为了不与 Tomcat 项目的 Session 混在 一起,它做了一层 Session 的抽象。Geronimo 在集成 WADI 时又做了一层 Session 的抽象。 多层抽象带来的好处是代码比较通用,缺点是效率会降低。 3. 在底层, Tomcat 与 JOnAS 都采用了 Tribes 组通信框架。WADI 目前支持 Tribes 与 JGroup 两种组通信框架,但目前 Geronimo 在集成 WADI 时只集成 Tribes 一种,并且 WADI 在组 通信框架上又做了一层抽象。GlassFishV2 采用了 Shoal, Shoal 在 JXTA 上又做了一层抽 象,JXTA 是 SUN 公司的一个 P2P 协议。可以这样看,组通讯框架是 P2P 的一个子集,JXTA 对于那些没有 IP 地址的结点(如一个机器两个 IP 的情况、内网机器的情况)提供了一 个虚拟的可定位的地址,这个优点相对于组框架更容易实现 Domain。 4. 在内存复制方面,只有 Tomcat 与 JOnAS 是两两复制的,GlassFish 是结对复制,而 Geronimo 深入理解各 JEE 服务器 Web 层集群原理 40 是取模复制。另外,GlassFish 除了支持内存复制之外,还支持基于 HADB 的集中式架构。 5. JBoss 与其他服务器的集群原理不一样,JBoss 是基于分布式缓存的(JBoss Cache)。另外, GlassFish 使用的 Shoal 也提供了分布式缓存的功能,WADI 目前也提供了还不算完善的分 布式缓存功能。 进行纯理论分析的话,个人觉得各服务器的集群的性能排名应该如下(当然,不可避免的这 会带有我个人的感觉色彩,仅供参考): 由于 JBoss 是基于分布式缓存的,原理与其他服务器不一样,暂时不参与排名。 1. 在小规模集群情况下(此时,两两复制这种拓扑不会成为瓶颈,而代码抽象层次多可能 会成为瓶颈): Tomcat、JOnAS、GlassFish 应该差不多,排第一位 Geronimo 由于多了两层抽象,从理论上讲,它应该排最后 2. 在大规模集群的情况下(此时,两两复制这种拓扑会成为瓶颈): GlassFish 由于是结对复制,排第一位 由于 Geronimo 是取模复制,我们设置它复制的次数和结对复制一样为 1,此时情况分 两种:  当拓扑的影响大于代码抽象的影响时,Geronimo 的性能好于 JOnAS 与 Tomcat  当拓扑的影响小于代码抽象的影响时,Geronimo 的性能差于 JOnAS 与 Tomcat 注:实际测试数据表明,这个临界点是 6 个集群结点。 6.2 实际测试数据结果 次数 集群大小 Session 大小 并发大小 Geronimo(TPS) Tomcat(TP S) JOnAS(TPS) 1 5 台 12.6K 10 2.23 352 2 5 台 50K 30 43.09 129.38 128.59 3 5 台 50M 50 内存溢出 4 5 台 50M 30 内存溢出 6 测试数据分析 41 5 5 台 50M 15 内存溢出 6 5 台 50M 8 内存溢出 7 5 台 50M 2 内存溢出 表 6-1 集群测试数据对比表 以上数据只是表明 JOnAS 的集群完全是基于 Tomcat 的,所以他们两个的数据表现一致。但 是 Geronimo 的性能为什么差还是没搞清楚。于是,我们进一步深入分析了代码,找到一个 重要线索: Geronimo 采用的是取模复制策略(取模复制是我定义的一个名词,可能并不确切),代码中 显示取模复制策略的算法是: Math.abs(key.hashCode()%_numPartitions) 其中,_numPartitions 指 Partition 的个数,关于 Partition 的解释请参见以上相关章节。于是, 我们调优了两个参数,位于 var/config/config-substitutions.properties 文件中:  DefaultWadiNumPartitions 令它等于集群结点数目,它默认值原来是 24,该值我认为就 是取模复制策略算法中的_numPartitions  ReplicaCount 令它等于 1,它默认值原本是 2,该值我认为是一个 Session 被复制的次数 那样,我们有了下列的测试数据(以下测试采用的测试数据均为 50K): 次数 集群大小 并发大 小 Replica Count DefaultWadiNum Partitions Geronimo (TPS) Tomcat (TPS) 1 4 55 2 24 151.16 2 4 70 2 24 147.58 3 4 100 2 24 142.19 4 4 115 2 24 153.58 5 4 125 2 24 17.17 147.71 6 4 125 1 4 83.98 7 5 125 1 5 128.98 161.51 深入理解各 JEE 服务器 Web 层集群原理 42 8 6 125 1 6 160.14 163.02 9 7 125 1 7 193.77 165 10 7 125 1 7 203.79 160.19 表 6-3 调整 Geronimo 取模参数后与 Tomcat 集群性能对比表 注:在网络流量方面,Tomcat 基本上都是在 99%左右,Geronimo 基本上都是在 30%-50%之 间。 以上测试数据充分表明: 在 DefaultWadiNumPartitions 与 ReplicaCount 参数按我们上面所说的方法修改的前提下,6 台机器是一个拐点,在 7 台机器集群的情况下,Geronimo 集群的性能就显现出来了。 6.3 测试过程中所发现的一些问题 1. Geronimo 集群各节点最好按照 Apache 服务器中的配置顺序启动,比如说一个节点坏了, 当这个节点又好了的时候,这个节点会偶尔性的启动不起来,当然了,并不是每次都这 样,只是间或、偶尔,这个时候,你必须将所有节点关闭,然后按顺序启动。Tomcat 与 JOnAS 不存在这种情况。 2. Geronimo 集群的某个结点会随时发生启动失败的情况,原因不明,解决的办法就是重 启机器,一次有时候可能还不行,按照经验,一般重启机器两次可解决。这时候报的错 有时如下: 6 测试数据分析 43 3. Tomcat 与 JOnAS 集群在快速刷新浏览器的情况下可能会报下列错误: 4. Geronimo 集群在快速刷新浏览器的情况下可能会报下列错误: 深入理解各 JEE 服务器 Web 层集群原理 44 5. JOnAS必须手工初始化,否则会报下列错误。我所指的手工初始化是指比如集群有两个 节点,应用服务器的端口为 8080, apache服务器的端口为 80 的话,你不能一开始就直 接访问 http://apache_ip/test/index.jsp 你应该依次先访问: http://appserver1_ip:8080/test.index.jsp http://appserver2_ip:8080/test.index.jsp 6 测试数据分析 45 最后再访问:http://apache_ip/test/index.jsp 产生这个问题的原因我想是:JOnAS是基于OSGI框架的,所有的模块都是bundle化的, 一个WEB应用部署后也是最终要bundle化的,虽然部署了,但是这个WEB应用的bundle 却没有加载,当直接访问 http://apache_ip/test/index.jsp时可能就会抛空指针,这应该是 JOnAS的一个BUG吧。 深入理解各 JEE 服务器 Web 层集群原理 46 7 结论与建议 以上理论分析、代码分析与测试结果表明:  JonAS 中的 Session 复制是完全基于 Tomcat 的,在这点上,它们是一回事  Jboss 是基于分布式缓存的,它与其他各服务器的集群原理不一样  Geronimo 的稳定性不好  集群规模小于 6 个节点时,Geronimo 的性能是远远不如 Tomcat 的,只有当集群规模大 于 6 个节点后,Geronimo 的性能才会超过 Tomcat。两者之间的优劣主要取决于实际应 用场景的集群机器数是大于 6 还是小于 6 注:以上数据分析未涉及 GlassFish 个人建议,在集群规模小于 6 台机器时,用 Tomcat 还是不错的; 如果集群规模大于 6 台机 器,可以使用 Glassfish。 深入理解各 JEE 服务器 ----Web 层集群原理 责任编辑:曹云飞 美术编辑:胡伟红 审校编辑:王丽娟、崔康 本迷你书主页为 http://www.infoq.com/cn/minibooks/jee- webserver-cluster 本书属于 InfoQ 企业软件开发丛书。 如果您打算订购 InfoQ 的图书,请联系 books@c4media.com 未经出版者预先的书面许可,不得以任何方式复制或者抄袭本书的任何 部分,本书任何部分不得用于再印刷,存储于可重复使用的系统,或者 以任何方式进行电子、机械、复印和录制等形式传播。 本书提到的公司产品或者使用到的商标为产品公司所有。 如果读者要了解具体的商标和注册信息,应该联系相应的公司。 所有内容版权均属© 2010 C4Media Inc.所有。 C4Media 是 InfoQ.com 这一企业软件开发社区的出版商。   1   

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享文档获得金币 ] 1 人已下载

下载文档

相关文档