Tomcat - 如何使用 PersistentManager + FileStore 将会话立即保存到磁盘 [英] Tomcat - How to persist a session immediately to disk using PersistentManager + FileStore

查看:96
本文介绍了Tomcat - 如何使用 PersistentManager + FileStore 将会话立即保存到磁盘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将 Tomcat 的 HttpSessions 持久化到磁盘,以便它可以在可扩展的云环境中使用.关键是将有许多 Tomcat 节点(在云 PaaS 中)并且客户端可以被定向到其中的任何一个.我们希望从共享磁盘机持久化和加载会话.

I want to persist Tomcat's HttpSessions to disk so that it can be used in a scalable cloud environment. The point is that there will be a number of Tomcat nodes up (in a cloud PaaS) and clients can be directed to any of them. We want to persist and load the sessions from a shared disk unit.

我以这种方式配置了 PersistentManager:

I have configured the PersistentManager this way:

context.xml

<Manager className="org.apache.catalina.session.PersistentManager">
   <Store className="org.apache.catalina.session.FileStore" directory="c:/somedir"/>
</Manager>

问题是会话显然从未刷新到磁盘.

The problem is that sessions are, apparently, never flushed to disk.

我更改了 配置,添加了 maxIdleBackup:

I changed the <Manager> config adding maxIdleBackup:

<Manager className="org.apache.catalina.session.PersistentManager maxIdleBackup="1">

这样我看到会话持续到磁盘需要将近一分钟的时间.奇怪的是,文档指出它应该需要大约一秒钟:

This way it takes almost a minute until I see the session persisted to disk. Oddly enough, the doc states that it should take around a second:

ma​​xIdleBackup:自上次访问以来的时间间隔(以秒为单位)在它有资格被持久化到会话之前的会话store 或 -1 禁用此功能.默认情况下,此功能是已禁用.

maxIdleBackup: The time interval (in seconds) since the last access to a session before it is eligible for being persisted to the session store, or -1 to disable this feature. By default, this feature is disabled.

其他配置:

按照 文档 我设置了系统属性

Following the documentation I set the system property

org.apache.catalina.session.StandardSession.ACTIVITY_CHECK -> true

有没有办法立即将会话刷新到磁盘?是否可以立即保留会话中的任何更改?

Is there a way to immediately flush the session to disk? Is is possible to make that any change in the session is also persisted right away?

更新:

我已尝试使用 maxIdleBackup="0" minIdleSwap="0" maxIdleSwap="1" 强制钝化会话并刷新到磁盘,但仍然需要将近一分钟.

I have tried to force the passivation of the session and flushing to disk with maxIdleBackup="0" minIdleSwap="0" maxIdleSwap="1", but it still takes almost a minute.

推荐答案

我终于设法解决了这个问题:

I finally managed to solve this:

  1. 我扩展了 org.apache.catalina.session.ManagerBase 覆盖使用超类会话映射的每个方法,以便它直接攻击文件(或缓存).
  1. I extended org.apache.catalina.session.ManagerBase overriding every method that used the superclass' sessions map, so that it attacked a file (or cache) directly.

示例:

@Override
public HashMap<String, String> getSession(String sessionId) {
    Session s = getSessionFromStore(sessionId);
    if (s == null) {
        if (log.isInfoEnabled()) {
            log.info("Session not found " + sessionId);
        }
        return null;
    }

    Enumeration<String> ee = s.getSession().getAttributeNames();
    if (ee == null || !ee.hasMoreElements()) {
        return null;
    }

    HashMap<String, String> map = new HashMap<>();
    while (ee.hasMoreElements()) {
        String attrName = ee.nextElement();
        map.put(attrName, getSessionAttribute(sessionId, attrName));
    }

    return map;

}

重要事项:

加载和卸载方法必须留空:

load and unload methods must be left empty:

    @Override
    public void load() throws ClassNotFoundException, IOException {
        // TODO Auto-generated method stub

    }

    @Override
    public void unload() throws IOException {
        // TODO Auto-generated method stub

    }

您必须覆盖 startInternal 和 stopInternal 以防止生命周期错误:

You have to override startInternal and stopInternal to prevent Lifecycle errors:

@Override
protected synchronized void startInternal() throws LifecycleException {

    super.startInternal();

    // Load unloaded sessions, if any
    try {
        load();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        log.error(sm.getString("standardManager.managerLoad"), t);
    }

    setState(LifecycleState.STARTING);
}

@Override
protected synchronized void stopInternal() throws LifecycleException {

    if (log.isDebugEnabled()) {
        log.debug("Stopping");
    }

    setState(LifecycleState.STOPPING);

    // Write out sessions
    try {
        unload();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        log.error(sm.getString("standardManager.managerUnload"), t);
    }

    // Expire all active sessions
    Session sessions[] = findSessions();
    for (int i = 0; i < sessions.length; i++) {
        Session session = sessions[i];
        try {
            if (session.isValid()) {
                session.expire();
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
        } finally {
            // Measure against memory leaking if references to the session
            // object are kept in a shared field somewhere
            session.recycle();
        }
    }

    // Require a new random number generator if we are restarted
    super.stopInternal();
} 

  1. 上面允许始终从文件(或缓存)中读取,但是写操作呢?.为此,我扩展了 org.apache.catalina.session.StandardSession 覆盖了 public void setAttribute(String name, Object value, boolean notify)public void removeAttribute(字符串名称,布尔通知).
  1. The above allows to read always from the file (or cache) but what about the write operations?. For this, I extended org.apache.catalina.session.StandardSession overriding public void setAttribute(String name, Object value, boolean notify) and public void removeAttribute(String name, boolean notify).

示例:

@Override
public void setAttribute(String name, Object value, boolean notify) {
    super.setAttribute(name, value, notify);
    ((DataGridManager)this.getManager()).getCacheManager().getCache("sessions").put(this.getIdInternal(), this);
}

@Override
public void removeAttribute(String name, boolean notify) {
    super.removeAttribute(name, notify);
    ((DataGridManager)this.getManager()).getCacheManager().getCache("sessions").put(this.getIdInternal(), this);
}

重要提示:

在我们的例子中,真正的会话备份最终是一个缓存(而不是文件),当我们从中读取扩展的 Tomcat 会话(在我们的 ManagerBase impl 类中)时,我们不得不以一种丑陋的方式调整它,以便一切正常:

In our case the real session backup ended up being a cache (not a file) and when we read the extended Tomcat session from it (in our ManagerBase impl class) we had to tweak it in an kind of ugly way so that everything worked:

    private Session getSessionFromStore(String sessionId){
        DataGridSession s = (DataGridSession)cacheManager.getCache("sessions").get(sessionId);
        if(s!=null){
            try {
                Field notesField;
                notesField = StandardSession.class.getDeclaredField("notes");
                notesField.setAccessible(true);
                notesField.set(s, new HashMap<String, Object>());
                s.setManager(this);
            } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
                throw new RuntimeException(e);
            }
        }
        return s;
    }

这篇关于Tomcat - 如何使用 PersistentManager + FileStore 将会话立即保存到磁盘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆