Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101971211
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: LINUX

2008-04-23 21:19:11

出处:ChinaITLab 
 
2.4.2 测试错误恢复
访问url: 可以得到一个关于session的例子,我们用它来测试集群的错误恢复能力。
测试步骤如下:
关闭tomcat1和tomcat2;
启动tomcat1
在浏览器中输入属性名tomcat1和属性值tomcat1再提交,返回的页面显示session中有刚刚输入的tomcat1属性;
启动tomcat2;
过一会后(等待tomcat2和tomcat1通信并复制信息)关闭tomcat1;
在浏览器中输入属性名tomcat2和属性值tomcat2再提交,返回的页面显示session中有刚刚输入的tomcat2属性,还有先前输入的tomcat1属性;
启动tomcat1;
过一会后(等待tomcat2和tomcat1通信并复制信息)关闭tomcat2;
在浏览器中输入属性名tomcat11和属性值tomcat11再提交,返回的页面显示session中有刚刚输入的tomcat11属性,还有先前输入的tomcat1和tomcat2属性;
……
2.4.3 测试多目传输的方法
如果运行测试失败,可以使用下面的JAVAGROUP方法测试机器的多目传输性:
启动多目接收器:
java org.javagroups.tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555
启动多目传输器:
java org.javagroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555
这样你在McastSenderTest窗口中输入内容,应该在McastReceiverWindow中可以看到结果。如果看不到结果,在 McastSenderTest运行参数中加入-ttl 32,如果还不行,可以修改多目地址再试试(注意避开系统保留用的多目地址);如果还不行,就去问问网管吧!
2.4.4 对tomcat-javagroups的修改
tomcat-javagroups.jar中的org.apache.catalina.session.ReplicatedSession类的removeAttribute方法会导致stackoverflow错误,请按下面的代码对其进行修改:
public void removeAttribute(String name, boolean notify, boolean jgnotify) {
super.removeAttribute(name);
if ( jgnotify )
{
SessionMessage msg =
new SessionMessage(notify?SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY:SessionMessage. EVT_ATTRIBUTE_REMOVED_WONOTIFY,
null,
getId(),
name,
null,
null);
sendMessage(msg);
}
}
public void removeAttribute(String name, boolean notify) {
removeAttribute(name,notify,true);
}
3 jetspeed集群
我们现在知道了如何配置、甚至拥有一个集群环境,接下来本文分析Jetspeed的集群现状,主要包括repository和Session数据;为了使分析具有目的,在分析Jetspeed的集群现状之前,先讲述了集群需求和RunData对象。读者可以用集群环境来验证和调试Jetspeed的集群功能。
3.1 集群要求
《Memory Session Replication》一文中讲述了支持集群的应用程序需注意的要点,现在对关于应用系统开发时应注意的事项总结如下:
保存在Session中的对象必须实现java.io.Serializable接口;
从session中获取对象修改后必须用session.setAttribute方法重置session中的属性,因为只有setAttribute能导致session复制。
Java VM不支持类变量的序列化,所以要注意failover不能依赖类变量;
保证各个服务实体的配置完全一样;
保证session状态是唯一决定当前任务状态的东西,临时文件、类变量等会使得错误恢复难以实现、行为可能琢磨不定;
利用request.setAttribute()保存当前请求级的状态,减少服务实体间通信次数。
尽量不要在session中保存大对象,提高服务实体间通信性能。
3.2 RunData对象
RunData对象概念来自于Turbine,在Jetspeed中RunData对象的类型是DefaultJetspeedRunData,这个类扩展了Turbine中的DefaultTurbineRunData类。Jetspeed系统接到用户浏览器的URL请求,进行计算和信息处理,最后返回给浏览器HTTP代码流的整个过程中的代码都可以访问同一个RunData对象。所以RunData对象是Jetspeed系统中各个代码模块共享信息的机制。
3.3 Jetspeed的Repository
Repository 一般指一个软件系统赖以启动、运行的持久性环境,包括启动Repository和运行Repository两部分。启动Repository用于决定系统启动时的参数,系统运行时不会改变它,如果改变了这些参数,软件系统必须重新启动;运行Repository指实时影响软件系统业务操作的参数,这些参数可以被用户或管理员当系统在线时改变。现在的趋势是:尽量减少启动Repository,而扩大运行Repository;针对Repository的修改最好能使用管理性框架,比如SNMP和JMX。Jetspeed的repository主要在Xreg、psml和Properties文件中实现。
Xreg是jetspeed的注册表,用于登记portlet、control、controller、skin、mediatype等原始资源的定义,jetspeed中缺省地把它实现为文件形式,各种类型有自己的注册表文件;
Psml 是门户结构标记语言的简称,用于组织xreg中的原始资源形成一个对门户视图的定义,当用户使用桌面浏览器访问jetspeed系统时,这个系统根据用户的URL定位一个Psml文档,接着解释这个文档形成HTML代码流返回给浏览器,浏览器展现这个代码流从而形成视窗化的门户视图。Jetspeed中包括了对psml的数据库和文件两种实现方式;
Properties定义了Jetspeed的重要服务及其参数,目前只有文件实现方式。
Jetspeed的启动Repository主要在Properties文件中,运行Repository在xreg和psml中。文件形式的实现大大阻碍了jetspeed支持集群的能力和表现,因为现在很少的应用服务器集群能在一个文件系统上运行,如果Repository需要在运行时改变,就必须同步多个服务实体上的文件,这是一个相当麻烦的问题。如果Repository支持数据库实现形式,Jetspeed可以充分利用数据库的存储和同步机制实现同一个Repository服务于多个Jetspeed。所以要想 jetspeed支持集群、拥有更佳表现,对Repository的数据库化是一个不可忽视的任务。
支持数据库的集群配置如下图:
 

这个图显示了在数据库集群环境下的jetspeed集群配置,数据库负载均衡器实现数据库集群的单一影像,例子有weblogic server中的multipool datasource,sql server 基于的windows 2000集群的单一集群IP,ORACLE RAC 的支持多连接地址的thin jdbc driver。
3.4 Jetspeed的Session数据
支持集群必须使得各个服务实体针对某个任务的执行环境是相同的,对于jetspeed来说就是针对各个URL请求,session的数据能在各个 jetspeed上复制。这些session被同一个sessionid所标识,这些标识可能来自浏览器的cookies或URL中。我们首先用一个 velocityportlet来显示Jetspeed的session中到底保存了什么数据,这个portlet的注册名字为 SessionPortlet。
3.4.1 SessionPortlet
SessionPortlet是一个velocityPortlet,其类名可以是CustomizerVelocityPortlet或 VelocityPortlet,一般情况下没有必要开发一个新的portlet class。关于如何开发部署portlet的教程可见参考部分,现在我们分注册、控制助手、portlet模版和运行来讲述这个portlet。
3.4.1.1 注册
SessionPortlet用于显示目前的session数据。它在xreg中的注册代码为:

3.4.1.2 控制助手Action
portlets.SessionAction是Velocityportlet模版portlet的控制助手,在velocity解释模版前执行:
public class SessionAction extends VelocityPortletAction {
protected void buildNormalContext( VelocityPortlet portlet,
Context context,
RunData rundata )
{
Map map = new HashMap();
Enumeration enumeration = rundata.getSession().getAttributeNames();
while (enumeration.hasMoreElements()) {
Object key = (Object) enumeration.nextElement();
Object value = (Object)rundata.getSession().getAttribute(key.toString());
map.put(key, value);
}
context.put("sessions",map);
}
}
从上面的代码可以看出,这个控制助手在模版的模型(MVC中的M)环境中设置了一个保存了session数据的map数据结构。
3.4.1.3 portlet模版
SessionPortlet的模版文件是session.vm(MVC中的V),这个文件的内容如下:

    #foreach( $key in $sessions.keySet() )
  • Key: $key -> Value: $sessions.get($key)
    #end

3.4.1.4 定制psml和运行SessionPortlet
用admin/jetspeed或turbine/turbine帐号/口令登录到jetspeed系统后,可以在velocity.legend portlet分类中找到SessionPortlet,把它加入到你的psml中后可以看到SessionPortlet显示的session数据(你可以多多点击其它的URL,尽量地使jetspeed在session中多放一些数据):
 

从上面的session快照可以看出,Jetspeed的session数据主要分为两类:BaseJetspeedUser和JetspeedHttpStateManagerService$StateEntry,下面我们就分别来看看这两个类的情况。
 

3.4.2 BaseJetspeedUser
我们从《Session数据类图(部分)》可以看出BaseJetspeedUser实现了serializable接口。另外分析这个类及其父类的代码可了解到这个类的成员也实现了serializable接口。所以可以初步得出这个类是集群安全的。
DefaultTurbineRundata实现了这个类型的session数据的操作接口:
保存user对象到session中,这个方法登录后由TurbineAuthentication的login调用,登录前由 JetspeedSessionValidator的doPerform调用,它们同时会调用DefaultTurbineRundata的 setUser方法:
public void save()
{
session.putValue(User.SESSION_KEY, (Object) user );
}
public void setUser(User user)
{
this.user = user;
}
从session中获取user对象数据,这个方法由JetspeedSessionValidator的doPerform调用:
public void populate()
{
user = getUserFromSession();
if ( user != null )
{
user.setLastAccessDate();
user.incrementAccessCounter();
user.incrementAccessCounterForSession();
}
}
public User getUserFromSession()
{
return getUserFromSession(session);
}
public static User getUserFromSession(HttpSession session)
{
try
{
return (User) session.getValue(User.SESSION_KEY);
}
catch ( ClassCastException e )
{
return null;
}
}
删除session中的用户数据,目前没地方调用:
public boolean removeUserFromSession()
{
return removeUserFromSession(session);
}
public static boolean removeUserFromSession(HttpSession session)
{
try
{
session.removeValue(User.SESSION_KEY);
}
catch ( Exception e )
{
return false;
}
return true;
}
阅读(319) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~