2015-11-03 61 views
0

我试图安装一个Tomcat集群上的AWS和自AWS不支持IP多播,选项之一是tomcat clustering using DBTomcat的Hazelcast会话存储会话属性消失

,非常然而理解,由于性能损失与数据库调用相关,我目前正在考虑将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> 

的会话正在被保存在Hazelcast实例并从Tomcat跟踪低于:

--------------------------------------------------------------------------------------- 
HC_SessionStore == Saving Session ID == C19A496F2BB9E6A4A55E70865261FC9F SESSION == StandardSession[ 
C19A496F2BB9E6A4A55E70865261FC9F] 
SESSION ATTRIBUTE :: USER_IDENTIFIER :: 50 
SESSION ATTRIBUTE :: APPLICATION_IDENTIFIER :: APPLICATION_1 
SESSION ATTRIBUTE :: USER_EMAIL :: [email protected] 
SESSION ATTRIBUTE :: USER_ROLES :: [PLATFORM_ADMIN, CLIENT_ADMIN, PEN_TESTER, USER] 
SESSION ATTRIBUTE :: CLIENT_IDENTIFIER :: 1 
--------------------------------------------------------------------------------------- 
03-Nov-2015 15:12:02.562 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca 
talina.session.PersistentManagerBase.processExpires End expire sessions PersistentManager processing 
Time 75 expired sessions: 0 
03-Nov-2015 15:12:02.563 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca 
talina.session.PersistentManagerBase.processExpires Start expire sessions PersistentManager at 14465 
43722563 sessioncount 0 
03-Nov-2015 15:12:02.577 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca 
talina.session.PersistentManagerBase.processExpires End expire sessions PersistentManager processing 
Time 14 expired sessions: 0 

上述跟踪如果从'保存'方法由商店实施覆盖,代码如下:

@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 

'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 

现在,最有趣的事情之一是,虽然“存储”方法被调用,在60秒的默认间隔, 'load'方法永远不会被调用,净影响是所有保存的会话属性在一段时间后都会丢失,这是最不寻常的。从技术上讲,绑定到会话的任何新会话属性都将保存在Hazelcast中,一旦调用“save”方法并且管理器被配置为每5秒换出一次。

但是,会话属性丢失(新的),旧的仍然存在。但不管它是什么“负载”方法都没有被调用(至少我没有看到跟踪)。

对此的一些帮助将非常感激。

+0

奇怪。 ofc你会做到这一点,但仍然要确认 - 你有打印报告/调试点,看看load()是否被击中? – Dinesh

+0

是的,有打印语句,在'load'中保存,请参考上面的代码,它也打印绑定到会话的属性,谢谢 – Ironluca

+1

我看到了代码。我特别指出了load()后的调试语句(我在这里没有看到)。由于readObject是一个阻塞呼叫。 – Dinesh

回答

0

希望这可以帮助别人,这个问题实际上是在下面的代码段:

公共无效保存(会话的会话)抛出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); 

公共会话负载(字符串的sessionId)抛出的ClassNotFoundException ,IOException的方法:

 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,这不是一个概率本身。

现在,如果我们看看StandardSession的Tomcat代码,我们可以看到它包含许多不会被序列化的瞬态属性。因此,在反序列化过程中,必须给这些属性赋予值,这是在'load'方法中完成的,但是,它的做法是错误的,首先将会话从ObjectInputStream的'readObjectData'方法反序列化以初始化瞬态属性。在StandardSession中,'readObjectData'调用'doReadObject'一个受保护的方法来重新初始化瞬态属性,而这又会期望提供的对象输入流是一系列对象。然而在我们的例子中,它是序列化的对象,而不是它期望的一系列对象。

事实上,在Tomcat上启用精细级别日志记录之后,只会看到此异常,否则不会。

解决方法很简单,StandardSession有一个方法'writeObjectData',它内部调用一个受保护的方法'doWriteObject',它将一系列对象中的会话状态写入输出流,读取此序列化的字节可解决问题。