0

AWS で Tomcat クラスターをセットアップしようとしています。AWS は IP マルチキャストをサポートしていないため、オプションの 1 つはDB を使用した Tomcat クラスター化です。

それはよく理解されていますが、DB 呼び出しに関連するパフォーマンスの低下により、私は現在、Hazelcast をセッション ストアとして検討しています。Web アプリに他のフィルターがあり、それらが多少干渉しているため、現在の Hazelcast フィルター アプローチはうまくいきません。より適切でクリーンなアプローチは、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 :: x@y.com
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

上記のトレースは、ストアの実装によってオーバーライドされた 'save' メソッドからのものであり、コードは以下のとおりです。

@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

最も興味深いことの 1 つは、「store」メソッドがデフォルトの 60 秒間隔で呼び出される一方で、「load」メソッドが呼び出されず、保存されたセッション属性がしばらくすると失われるという正味の影響があることです。 、これは最も珍しいことです。技術的には、セッションにバインドされた新しいセッション属性は、「save」メソッドが呼び出され、マネージャーが 5 秒ごとにスワップアウトするように構成されていると、Hazelcast に保存されます。

ただし、セッション属性は失われ (新しいもの)、古いものはまだ残っています。しかし、それが何であれ、「ロード」メソッドは呼び出されません (少なくともトレースは表示されません)。

これに関するいくつかの助けは本当にありがたいです。

4

1 に答える 1

0

これが誰かの役に立てば幸いです。問題は実際には次のコードセクションにあります。

public void save(Session session) は 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);

public Session load(String 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 には、保護されたメソッド「doWriteObject」を内部的に呼び出すメソッド「writeObjectData」メソッドがあり、一連のオブジェクトのセッション状態を出力ストリームに書き込み、このシリアル化されたバイトを読み取ることで問題が解決します。

于 2015-11-16T12:06:48.307 に答える