Session Tomcat Hazelcast会话存储会话属性消失
我正在尝试在AWS上设置Tomcat集群,因为AWS不支持IP多播,其中一个选项是 这是众所周知的,但是,由于与DB调用相关的性能损失,我目前正在考虑将Hazelcast作为会话存储。当前的Hazelcast过滤器方法不适合我,因为web应用程序上还有其他过滤器,它们有些干扰,更好、更干净的方法是使用自定义存储实现配置PersistenceManager,并在tomcat/conf context.xml上配置相同的过滤器,配置部分如下所示:Session Tomcat Hazelcast会话存储会话属性消失,session,tomcat,hazelcast,hazelcast-imap,Session,Tomcat,Hazelcast,Hazelcast Imap,我正在尝试在AWS上设置Tomcat集群,因为AWS不支持IP多播,其中一个选项是 这是众所周知的,但是,由于与DB调用相关的性能损失,我目前正在考虑将Hazelcast作为会话存储。当前的Hazelcast过滤器方法不适合我,因为web应用程序上还有其他过滤器,它们有些干扰,更好、更干净的方法是使用自定义存储实现配置PersistenceManager,并在tomcat/conf context.xml上配置相同的过滤器,配置部分如下所示: <Manager className="
<Manager className="org.apache.catalina.session.PersistentManager"
distributable="true"
maxActiveSessions="-1"
maxIdleBackup="2"
maxIdleSwap="5"
processingTime="1000"
saveOnRestart="true"
maxInactiveInterval="1200">
<Store className="com.hm.vigil.platform.session.HC_SessionStore"/>
</Manager>
@Override
public void save(Session session) throws IOException {
//System.out.println("HC_SessionStore == Saving Session ID == "+session.getId()+" SESSION == "+session);
try{
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
sessionCounter++;
System.out.println("---------------------------------------------------------------------------------------");
System.out.println("HC_SessionStore == Saving Session ID == "+sessionId+" SESSION == "+session);
Enumeration<String> attributeNames=((StandardSession)session).getAttributeNames();
while(attributeNames.hasMoreElements()){
String attributeName=attributeNames.nextElement();
System.out.println("SESSION ATTRIBUTE :: "+attributeName+" :: "+((StandardSession)session).getAttribute(attributeName));
}//while closing
System.out.println("---------------------------------------------------------------------------------------");
}catch(Exception e){throw new IOException(e);}
}//save closing
上述跟踪如果来自存储实现覆盖的“save”方法,则代码如下所示:
<Manager className="org.apache.catalina.session.PersistentManager"
distributable="true"
maxActiveSessions="-1"
maxIdleBackup="2"
maxIdleSwap="5"
processingTime="1000"
saveOnRestart="true"
maxInactiveInterval="1200">
<Store className="com.hm.vigil.platform.session.HC_SessionStore"/>
</Manager>
@Override
public void save(Session session) throws IOException {
//System.out.println("HC_SessionStore == Saving Session ID == "+session.getId()+" SESSION == "+session);
try{
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
sessionCounter++;
System.out.println("---------------------------------------------------------------------------------------");
System.out.println("HC_SessionStore == Saving Session ID == "+sessionId+" SESSION == "+session);
Enumeration<String> attributeNames=((StandardSession)session).getAttributeNames();
while(attributeNames.hasMoreElements()){
String attributeName=attributeNames.nextElement();
System.out.println("SESSION ATTRIBUTE :: "+attributeName+" :: "+((StandardSession)session).getAttribute(attributeName));
}//while closing
System.out.println("---------------------------------------------------------------------------------------");
}catch(Exception e){throw new IOException(e);}
}//save closing
@覆盖
public void save(会话)引发IOException{
//System.out.println(“HC_SessionStore==保存会话ID==”+会话.getId()+“会话==”+会话);
试一试{
字符串sessionId=session.getId();
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
ObjectOutputStream oos=新的ObjectOutputStream(BAS);
oos.writeObject(会话);
oos.close();
字节[]serializedSession=bas.toByteArray();
sessionStore.put(sessionId,serializedSession);
sessionCounter++;
System.out.println(“-------------------------------------------------------------------------------------------------------------------------”;
System.out.println(“HC_SessionStore==保存会话ID==”+会话ID+“会话==”+会话);
枚举attributeNames=((StandardSession)会话).getAttributeNames();
while(attributeNames.hasMoreElements()){
字符串attributeName=attributeName.nextElement();
System.out.println(“会话属性:“+attributeName+”::“+((标准会话)会话).getAttribute(attributeName));
}//关门时
System.out.println(“-------------------------------------------------------------------------------------------------------------------------”;
}catch(异常e){抛出新的IOException(e);}
}//保存结束
其中“sessionStore”是Hazelcast分布式地图
存储的相应“加载”方法如下所示:
@Override
public Session load(String sessionId) throws ClassNotFoundException, IOException {
Session session=null;
try{
byte[] serializedSession=(byte[])sessionStore.get(sessionId);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
//Read the saved session from serialized state
//StandardSession session_=new StandardSession(manager);
StandardSession session_=(StandardSession)ois.readObject();
session_.setManager(manager);
ois.close();
//Initialize the transient properties of the session
ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
session_.readObjectData(ois);
session=session_;
ois.close();
System.out.println("===========================================================");
System.out.println("HC_SessionStore == Loading Session ID == "+sessionId+" SESSION == "+session);
Enumeration<String> attributeNames=session_.getAttributeNames();
while(attributeNames.hasMoreElements()){
String attributeName=attributeNames.nextElement();
System.out.println("SESSION ATTRIBUTE :: "+attributeName+" :: "+session_.getAttribute(attributeName));
}//while closing
System.out.println("===========================================================");
}catch(Exception e){throw new IOException(e);}
return session;
}//load closing
@覆盖
公共会话加载(字符串sessionId)抛出ClassNotFoundException、IOException{
会话=空;
试一试{
字节[]serializedSession=(字节[])sessionStore.get(sessionId);
ObjectInputStream ois=新ObjectInputStream(new ByteArrayInputStream(serializedSession));
//从序列化状态读取保存的会话
//StandardSession会话=新的StandardSession(管理器);
StandardSession会话=(StandardSession)ois.readObject();
会话设置经理(经理);
ois.close();
//初始化会话的临时属性
ois=新的ObjectInputStream(新的ByteArrayInputStream(serializedSession));
session.readObjectData(ois);
会话=会话;
ois.close();
System.out.println(“===========================================================================================”);
System.out.println(“HC_SessionStore==加载会话ID==”+会话ID+“会话==”+会话);
枚举attributeNames=session.getAttributeNames();
while(attributeNames.hasMoreElements()){
字符串attributeName=attributeName.nextElement();
System.out.println(“会话属性::”+attributeName+”::“+SESSION_u.getAttribute(attributeName));
}//关门时
System.out.println(“===========================================================================================”);
}catch(异常e){抛出新的IOException(e);}
返回会议;
}//负荷关闭
现在,最有趣的事情之一是,虽然“store”方法以默认的60秒间隔被调用,但“load”方法从未被调用,其净影响是保存的任何会话属性在一段时间后丢失,这是最不寻常的。从技术上讲,一旦调用“保存”方法,并且管理器配置为每5秒交换一次,绑定到会话的任何新会话属性都将保存在Hazelcast中
但是,会话属性丢失(新属性),旧属性仍然存在。但不管它是什么,都不会调用“load”方法(至少我看不到跟踪)
我们将非常感谢您在这方面提供的帮助。希望这对其他人有所帮助,问题实际上存在于以下代码部分: public void save(会话)抛出IOException方法:
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
byte[] serializedSession=(byte[])sessionStore.get(sessionId);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
//Read the saved session from serialized state
//StandardSession session_=new StandardSession(manager);
StandardSession session_=(StandardSession)ois.readObject();
session_.setManager(manager);
ois.close();
//Initialize the transient properties of the session
ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
session_.readObjectData(ois);
session=session_;
ois.close();
公共会话加载(字符串sessionId)抛出ClassNotFoundException,IOException方法:
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
byte[] serializedSession=(byte[])sessionStore.get(sessionId);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
//Read the saved session from serialized state
//StandardSession session_=new StandardSession(manager);
StandardSession session_=(StandardSession)ois.readObject();
session_.setManager(manager);
ois.close();
//Initialize the transient properties of the session
ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
session_.readObjectData(ois);
session=session_;
ois.close();
如果您注意到,会话被摘要序列化并保存到Hazelcast,这本身不是问题
现在,如果我们查看的Tomcat代码,我们会看到它包含许多不会被序列化的瞬态属性。因此,在反序列化过程中,必须为这些属性指定值,这是在“load”方法中完成的,但是,这是错误的,首先它从ObjectInputStream“readObjectData”方法反序列化会话以初始化瞬态属性。在StandardSession中,“readObjectData”调用“doReadObject”这一受保护的方法来重新初始化瞬态属性,而瞬态属性又期望提供的对象输入流是一系列对象。然而,在我们的例子中,它是整个序列化对象,而不是它所期望的对象序列
事实上,在Tomcat上启用精细级别日志记录后,只会看到此异常,而不会看到其他异常
解决方法很简单,StandardSession有一个方法“writeObjectData”方法,该方法在内部调用一个受保护的方法“doWriteObject”,该方法将一系列对象中的会话状态写入