Session Tomcat Hazelcast会话存储会话属性消失

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="

我正在尝试在AWS上设置Tomcat集群,因为AWS不支持IP多播,其中一个选项是

这是众所周知的,但是,由于与DB调用相关的性能损失,我目前正在考虑将Hazelcast作为会话存储。当前的Hazelcast过滤器方法不适合我,因为web应用程序上还有其他过滤器,它们有些干扰,更好、更干净的方法是使用自定义存储实现配置PersistenceManager,并在tomcat/conf context.xml上配置相同的过滤器,配置部分如下所示:

   <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”,该方法将一系列对象中的会话状态写入