2015-11-03 77 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',它將一系列對象中的會話狀態寫入輸出流,讀取此序列化的字節可解決問題。