Tomcat 9 源码分析(5)— Session管理

本文深入解析Tomcat 9中的Session管理,包括Session接口、Session管理器(如StandardManager的初始化和启动)、Session分配、Session追踪以及Session销毁的过程。通过对Tomcat源码的分析,了解Session如何在内存中存储、持久化以及集群环境下的同步策略。
摘要由CSDN通过智能技术生成

Session管理

Session可以用来管理用户的会话信息,最常见的就是拿Session来存放用户登录、身份、权限及状态等信息。对于使用Tomcat作为Web容器的大部分开发人员而言,本文介绍分析Tomcat是如何实现Session标记用户和管理Session信息

Session接口

Tomcat内部定义了SessionHttpSession这两个会话相关的接口,其类继承体系如下

avator

Session:Tomcat中有关会话的基本接口规范,下面介绍它定义的主要方法

方法 描述
getCreationTime()/setCreationTime() 获取与设置Session的创建时间
getId()/setId() 获取与设置Session的ID
getThisAccessedTime() 获取最近一次请求的开始时间
getLastAccessedTime() 获取最近一次请求的完成时间
getManager()/setManager() 获取与设置Session管理器
getMaxInactiveInterval() 获取Session的最大访问间隔
setMaxInactiveInterval() 设置Session的最大访问间隔
getSession() 获取HttpSession
setValid()/isValid() 获取与设置Session的有效状态
access()/endAccess() 开始与结束Session的访问
expire() 设置Session过期

HttpSession:在HTTP客户端与HTTP服务端提供的一种会话的接口规范

方法 描述
getCreationTime() 获取Session的创建时间
getId() 获取Session的ID
getLastAccessedTime() 获取最近一次请求的完成时间
getServletContext() 获取当前Session所属的ServletContext
getMaxInactiveInterval() 获取Session的最大访问间隔
setMaxInactiveInterval() 设置Session的最大访问间隔
getAttribute()/setAttribute() 获取与设置Session作用域的属性
removeAttribute() 清除Session作用域的属性
invalidate() 使Session失效并解除任何与此Session绑定的 对象

ClusterSession:集群部署下的会话接口规范

方法 描述
isPrimarySession() 判断是否为集群的主Session
setPrimarySession() 设置集群主Session

StandardSession:标准的HTTP Session实现,本文将以此实现为例展开

DeltaSession:Tomcat集群会话同步的策略,对会话中增量修改的属性进行同步。这种方式由于是增量的,所以会大大降低网络I/O的开销,但是是线上会比较复杂因为涉及到对会话属性操作过程的管理

ReplicationSessionListener:Tomcat集群会话同步的策略,每次都会把整个会话对象同步给集群中的其他节点,其他节点然后更新整个会话对象。这种实现比较简单但会造成大量无效信息的传输

Session管理器

Tomcat内部定义了Manager接口用于制定Session管理器的接口规范,目前已经有很多Session管理器的实现

avator

Manager:Tomcat对于Session管理器定义的接口规范

方法 描述
getContext()/setContext() 获取与设置上下文
getSessionIdGenerator() 获取会话id生成器
setSessionIdGenerator() 设置会话id生成器
getSessionCounter() 获取Session计数器
setSessionCounter() 设置Session计数器
getMaxActive()/setMaxActive() 获取与设置处于活动状态的最大会话数
getActiveSessions() 获取处于活跃状态的会话数
getExpiredSessions() 获取过期的会话数
setExpiredSessions() 设置过期的会话数
getRejectedSessions() 获取未创建的会话数
getSessionMaxAliveTime() 获取会话存活的最长时间(单位为秒)
setSessionMaxAliveTime() 设置会话存活的最长时间(单位为秒)
getSessionAverageAliveTime() 获取会话平均存活时间
getSessionCreateRate() 获取当前会话创建速率
getSessionExpireRate() 获取当前会话过期速率
add() 将此会话添加到处于活动状态的会话集合
addPropertyChangeListener() 将属性更改监听器到此组件
changeSessionId() 将当前会话的ID更改为新的随机生成的会话ID
rotateSessionId() 将当前会话的ID更改为新的随机生成的会话ID
changeSessionId() 将当前会话的ID更改为指定的会话ID
createEmptySession() 从回收的会话中获取会话或创建一个新的会话
createSession() 根据默认值构造并返回一个新的会话对象
findSession() 返回与此管理器关联的会话
findSessions() 返回与此管理器关联的会话集合
load()/unload() 从持久化机制中加载Session或向持久化机制写入Session
remove() 从此管理器的活动会话中删除此会话
removePropertyChangeListener() 从此组件中删除属性更改监听器
backgroundProcess() 容器接口中定义为具体容器在后台处理相关工作的实现,Session管理器基于此机制实现了过期Session的
willAttributeDistribute() 管理器写入指定的会话属性

ManagerBase:封装了Manager接口通用实现的抽象类,未提供对load()/unload()等方法的实现,需要具体子类去实现。所有的Session管理器都集成自ManagerBase

ClusterManager:在Manager接口的基础上增加了集群部署下的一些接口,所有实现集群下Session管理器都要实现此接口

PersistentManagerBase:提供了对于Session持久化的基本实现

PersistentManager:继承自PersistentManagerBase,可以在Server.xml的元素下通过配置元素来使用。PersistentManager可以将内存中的Session信息备份到文件或数据库中。当备份一个Session对象时,该Session对象会被复制到存储器(文件或者数据库)中,而原对象仍然留在内存中。因此即便服务器宕机,仍然可以从存储器中获取活动的Session对象。如果活动的Session对象超过了上限值或者Session对象闲置了的时间过长,那么Session会被换出到存储器中以节省内存空间

StandardManager:不用配置元素,当Tomcat正常关闭,重启或Web应用重新加载时,它会将内存中的Session序列化到Tomcat目录的/work/Catalina/host_name/webapp_name/SESSIONS.ser文件中。当Tomcat重启或者应用加载完成后,Tomcat会将文件中的Session重新还原到内存中。如果突然中止该服务器,则所有Session豆浆丢失,因为StandardManager没有机会实现存盘处理

ClusterManagerBase:提供了对于Session的集群管理实现

DeltaManager:继承自ClusterManagerBase。此Session管理器是Tomcat集群部署下的默认管理器,当集群中的某一节点生成或修改Session后,DeltaManager将会把这些修改增量复制到其他节点

BackupManager:没有继承ClusterManagerBase,而是直接实现了ClusterManager接口。是Tomcat在集群部署下的可选的Session管理器,集群中的所有Session都被全量复制到一个备份节点。集群中的所有节点都可以访问此备份节点,达到Session在集群下的备份效果

本文以StandardManager为例讲解Session的管理。StandardManager是StandardContext的子组件,用来管理当前Context的所有Session的创建和维护。由Tomcat生命周期管理可知,当StandardContext正式启动,也就是StandardContext的startInternal方法被调用时,StandardContext还会启动StandardManager

org.apache.catalina.core.StandardContext.startInternal()

@Override
protected synchronized void startInternal() throws LifecycleException {
   

    // 省略与Session管理无关的代码

            // Acquire clustered manager
            Manager contextManager = null;
            Manager manager = getManager();
            if (manager == null) {
   
                if (log.isDebugEnabled()) {
   
                    log.debug(sm.getString("standardContext.cluster.noManager",
                            Boolean.valueOf((getCluster() != null)),
                            Boolean.valueOf(distributable)));
                }
                if ((getCluster() != null) && distributable) {
   
                    try {
   
                        contextManager = getCluster().createManager(getName());
                    } catch (Exception ex) {
   
                        log.error(sm.getString("standardContext.cluster.managerError"), ex);
                        ok = false;
                    }
                } else {
   
                    contextManager = new StandardManager();
                }
            }

            // Configure default manager if none was specified
            if (contextManager != null) {
   
                if (log.isDebugEnabled()) {
   
                    log.debug(sm.getString("standardContext.manager",
                            contextManager.getClass().getName()));
                }
                setManager(contextManager);
            }

            if (manager!=null && (getCluster() != null) && distributable) {
   
                //let the cluster know that there is a context that is distributable
                //and that it has its own manager
                getCluster().registerManager(manager);
            }
        }

       // 省略与Session管理无关的代码

        try {
   
            // Start manager
            Manager manager = getManager();
            if (manager instanceof Lifecycle) {
   
                ((Lifecycle) manager).start();
            }
        } catch(Exception e) {
   
            log.error(sm.getString("standardContext.managerFail"), e);
            ok = false;
        }

        // 省略与Session管理无关的代码
}

从中可知StandardContext.startInternal()中涉及Session管理的执行步骤如下:

  1. 创建StandardManager
  2. 如果Tomcat结合Apache做了分布式部署,会将当前StandardManager注册到集群中
  3. 启动StandardManager

StandardManger.start()用于启动StandardManager

org.apache.catalina.util.LifecycleBase.start()

@Override
public final synchronized void start() throws LifecycleException {
   

    // 省略状态校验的代码

    if (state.equals(LifecycleState.NEW)) {
   
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
   
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
   
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
   
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
   
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
   
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
   
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
   
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

从中可知启动StandardManager的步骤如下:

  1. 调用init方法初始化StandardManager
  2. 调用startInternal方法启动StandardManager

StandardManager的初始化

经上分析可知,启动StandardManager的第一部就是调用父类LifecycleBase的init方法,init方法在Tomcat生命周期管理中已介绍,现在只需要关心StandardManager的initInternal。StandardManager本身并没有实现initInternal方法,但是StandardManager的父类ManagerBase实现了此方法,将StandardManager注册为到 JMX

org.apache.catalina.session.ManagerBase.initInternal()

@Override
protected void initInternal() throws LifecycleException {
   
    super.initInternal();

    if (context == null) {
   
        throw new LifecycleException(sm.getString("managerBase.contextNull"));
    }
}

StandardManager的启动

调用StandardManager的startInternal方法用于启动StandardManager

org.apache.catalina.session.StandardManager.startInternal()

@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")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值