每天学习一点点之 Tomcat 是如何清除过期 Session 的

今天使用一种很临时的方案解决 Session 泄漏的问题:缩短 Session 的过期时间。这种方法虽然简单,但却非常有效。然而,这引发了一个问题:我们应该将过期时间设置为多短呢?在 Spring Boot 中,最短的过期时间是 60 秒。如果你配置的值小于 60 秒,系统会将其默认设置为 60 秒。这是由 org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getSessionTimeoutInMinutes 方法决定的:

	private long getSessionTimeoutInMinutes() {Duration sessionTimeout = getSession().getTimeout();if (isZeroOrLess(sessionTimeout)) {return 0;}return Math.max(sessionTimeout.toMinutes(), 1);}

这时候组内有同学说,过短的过期时间是否会影响服务的性能,例如,是否需要更频繁地删除 Session

为了解答这个问题,我们需要理解 “Tomcat 是如何清除 Session 的”,以及 “Tomcat 中 Session 的更新机制是否会受到 Session 过期时间设置的长短的影响”。

其实根据经验,结合 Redis 删除过期 Key,以及 ThreadLocal 清除过期 entry 等存在类似场景的框架,过期清除机制无非就是两种策略,以及一些细微的处理差异:

  • 惰性删除:只在数据被访问时才检查和删除过期的数据
    • 每次只检查当前数据
    • 顺带检查周边数据
  • 定期删除:后台定时进行扫描
    • 全量扫描
    • 随机扫描

我相信 Tomcat 的处理方式也不会脱离这两种策略。带着这个思考,那么接下来就源码分析一下 Tomcat 到底是如何清除过期 Session 的。

关于 Session,Tomcat 提供了一个常用的函数 org.apache.catalina.session.StandardSession#isValid,用于判断 Session 是否有效。如果 Session 已经无效,该函数会调用 org.apache.catalina.session.StandardSession#expire(boolean) 使 Session 过期(移除)。这表明 Tomcat 对 Session 的处理采用了惰性删除机制。

Tomcat 提供了几种不同的 Session 持久化策略,主要通过实现不同的 ManagerStore 来实现:

  1. 内存持久化:这是 Tomcat 的默认策略,由 StandardManager 实现。所有的 Session 都保存在内存中。当 Tomcat 重启或者出现故障时,所有的 Session 信息都会丢失。
  2. 文件持久化:由 PersistentManagerFileStore 实现。不活跃的 Session 会被保存到文件系统中,从而释放内存。当 Session 再次变得活跃时,可以从文件系统中恢复。
  3. 数据库持久化:由 PersistentManagerJDBCStore 实现。不活跃的 Session 会被保存到数据库中。这种策略适合于需要在多个 Tomcat 实例之间共享 Session 的情况。
  4. 分布式 Session:在一个 Tomcat 集群中,可以通过实现 ClusterableSessionDeltaManagerBackupManager 来实现 Session 的复制或者备份,从而实现 Session 的分布式管理。

本文关注的是内存持久化策略,即 StandardManager。它有一个函数 org.apache.catalina.session.ManagerBase#processExpires,该函数遍历所有的 Session,对每个 Session 判断是否过期。如果发现 Session 已失效,就会让 Session 过期:

    public void processExpires() {long timeNow = System.currentTimeMillis();Session sessions[] = findSessions();int expireHere = 0;if (log.isDebugEnabled()) {log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);}for (Session session : sessions) {if (session != null && !session.isValid()) {expireHere++;}}long timeEnd = System.currentTimeMillis();if (log.isDebugEnabled()) {log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) +" expired sessions: " + expireHere);}processingTime += (timeEnd - timeNow);}

既然怀疑这个函数可能存在定时调度,其实有一个小技巧,在这个函数上打一个断点,看是否可能触发,果然没一会就触发了:

在这里插入图片描述

根据 stack trace,这个函数是由 ContainerBackgroundProcessor 执行的。

在这里插入图片描述

Tomcat 启动时会执行 threadStart 方法,该方法基于 java.util.concurrent.ScheduledExecutorService 启动一个定时任务,backgroundProcessorDelay 参数可以控制启动后多久开始执行,backgroundProcessorDelay 控制多久执行一次。

    /*** Start the background thread that will periodically check for session timeouts.*/protected void threadStart() {if (backgroundProcessorDelay > 0 &&(getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) &&(backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) {if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) {// There was an error executing the scheduled task, get it and log ittry {backgroundProcessorFuture.get();} catch (InterruptedException | ExecutionException e) {log.error(sm.getString("containerBase.backgroundProcess.error"), e);}}backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor().scheduleWithFixedDelay(new ContainerBackgroundProcessor(), c,backgroundProcessorDelay, TimeUnit.SECONDS);}}

这表明 Tomcat 对 Session 的处理也有定期删除机制。

总结

在 Tomcat 中,Session 的管理采用了惰性删除和定期删除两种策略:

  • 惰性删除是通过 org.apache.catalina.session.StandardSession#isValid 方法实现的,每次在判断 Session 是否有效的时候,如果 Session 已经无效,就会让 Session 过期(移除)
  • 在内存持久化策略中在内存持久化策略中,定期删除是通过 org.apache.catalina.session.ManagerBase#processExpires 方法实现的,该方法会定期遍历所有的 Session,对每个 Session 判断是否过期,如果发现 Session 已失效,就会让 Session 过期

在 Tomcat 中,Session 的更新机制并不会直接受到 Session 过期时间设置的长短的影响,但是,如果 Session 的数量过多,可能导致 Session 清理操作的效率降低。因为在每次 Session 清理操作时,都需要遍历所有的 Session,检查每个 Session 是否过期,如果过期就将其移除。如果 Session 的数量过多,那么这个过程就会消耗更多的时间和资源。

因此,虽然 Session 的过期时间设置不会直接影响 Session 的更新机制,但是 Session 的数量过多确实会对 Session 的更新效率和系统性能产生影响。为了保持系统的高效运行,应该尽量控制 Session 的数量,避免 Session 的数量过多。

欢迎关注公众号:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/163229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实在智能携“TARS大模型”入选“2023中国数据智能产业AI大模型先锋企业”

近日,由数据猿与上海大数据联盟联合主办的“2023企业数智化转型升级发展论坛”在上海圆满收官。 论坛颁奖典礼上,《2023中国数据智能产业AI大模型先锋企业》等六大榜单正式揭晓,旨在表彰在AI领域为数智化升级取得卓越成就和突出贡献的企业&am…

解决“使用 CNKI 保存时发生错误。改为尝试用 DOI 保存。”【Bug Killed】

文章目录 简介解决办法跟新本地Zotero中茉莉花插件的非官方维护中文翻译器更新网页插件Zetero Connector中的Transtors 结语参考资料 简介 使用Chrome ➕ Zotero Connector保存中国知网(CNKI)的参考文献到本地的Zotero时无法正常保存,出现使…

Altium Designer学习笔记8

创建原理图元件: 画出原理图: 根据规则书画出原理图: 根据规则书画出封装图: 参照: 确认下过孔的内径和外径的最小允许值。

Vatee万腾的数字时代探险:vatee科技力量的未来洞悉

在数字化的时代潮流中,Vatee万腾以其强大的科技力量,正在进行一场前所未有的数字时代探险。 Vatee万腾的数字时代探险源于其对未来的洞悉。通过深度研究和前瞻性思考,他们将科技力量与未来趋势相结合,勾勒出数字时代的新蓝图&…

springboot注解@NotNull,@NotBlank,@Valid自动判定空值

一.前言 使用springboot搭建项目时,我们都是采用的Restful风格接口,这里面问题来了,当前端调用接口或者是其他项目调用时,传入参数时我们不能单一靠调用方来控制参数的准确性,自己也要一些参数进行判断,进行非空之类的…

露营管理系统预约小程序效果如何

旅游经济已经复苏,并且市场规模增速加快,近一年来远途/周边游客户增多,不少旅游景区在节假日常常面对客流爆满现象。同时露营作为近几年突然火热的项目,其需求也是日渐上升。 然而在高需求的同时,我们也看到露营经营痛…

【数组栈】实现

目录 栈的概念及其结构 栈的实现 数组栈 链式栈 栈的常见接口实现 主函数Test.c 头文件&函数声明Stack.h 头文件 函数声明 函数实现Stack.c 初始化SLInit 扩容Createcapacity 压栈STPush 出栈STPop 栈顶元素STTop 判断栈是否为空STempty 栈内元素个数STSzi…

Mysql中join on中的like使用

1、使用mysql中的函数CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。 SELECT * FROM Table1 INNER JOIN Table2 ON Table1.col LIKE CONCAT(%, Table2.col, %) 2、放弃使用join语句 SELECT * FROM Table1, T…

使用sqlserver备份还原,复制迁移数据库

文章目录 前言一、备份数据库二、还原数据库三、其他 前言 当初学sqlserver复制数据库的时候,老师只教了右键数据库生成sql脚本,没说数据库非常大的时候咋搞啊,分离数据库复制一份后在附加上去太危险了 百度一下备份还原数据库针对小白的资料…

docker安装mysql及主从配置

创建mysql配置文件:my.cnf 主库配置: [client] ## 默认编码格式 default-character-setutf8mb4 [mysqld] ## 设置server-id,同一局域网中需要唯一 server-id101 ## 设置编码格式 character-set-serverutf8mb4 ## 允许最大连接数 max_conne…

Redis key键

Redis 是一种键值(key-value)型的缓存型数据库,它将数据全部以键值对的形式存储在内存中,并且 key 与 value 一一对应。这里的 key 被形象的称之为密钥,Redis 提供了诸多操作这把“密钥”的命令,从而实现了…

财报解读:电商GMV增长30%后,快手将坚守本地生活?

快手逐渐讲好了其高质量成长的故事。 根据财报,快手三季度业绩超出预期,其中,营收279.5亿元,同比增长20.8%;调整后净利润31.7亿元,同比扭亏为盈。 而联系市场环境来看,三季度广告、电商市场较…

超详细的pytest玩转HTML报告:修改、汉化和优化

前言 Pytest框架可以使用两种测试报告,其中一种就是使用pytest-html插件生成的测试报告,但是报告中有一些信息没有什么用途或者显示的不太好看,还有一些我们想要在报告中展示的信息却没有,最近又有人问我pytest-html生成的报告&a…

javascript Math相关计算取值属性方法

*向上取整【只要有小数就+1】 Math.ceil(3.14); // 4 *向下取整【有小数就舍弃】 Math.floor(3.14); // 3 parseInt(3.14); // 3 // 常用于字符串类型的数字转为十进制的数据 四舍五入【小数点后部分】 Math.round(11.5)); //12 Math.round(-11.5)); //-11 取两数…

6-使用nacos作为注册中心

本文讲解项目中集成nacos,并将nacos作为注册中心使用的过程。本文不涉及nacos的原理。 1、项目简介 以一个演示项目为例,项目包含三个服务,调用及依赖如下图: 由图中可以看出,coupon-customer-serv为服务的消费者&a…

基于element自动表格

需求是根据JSON文件生成表格,包含配置和自动props属性以及过滤器; 数据示例: 表格设置: /*** 表格表头信息* chineseToPinYin: 这是封装的根据中文汉字转换为拼音的方法* prop: 表头字段名* filter: 数据过滤器* label: 表头显示…

最长连续序列【中等】

leetcode链接 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1:输入:nums [100,4,200,1,3,2] 输出&am…

『new Date 在 IOS 失效 の bug』

问题:new Date()在安卓下正常,在IOS下显示不出来。 原因:在IOS下,new Date(“2000-2-22 00:10”),返回的是undefined,因为IOS不支持这种类型格式。 解决:更换下格式:new Date(“2000/2/22”) …

类初始化,类加载,类加载器

类初始化,类加载,类加载器 1. 类加载1.1. 类的加载1.2. 类的链接1.2.1. 验证1.2.2. 准备1.2.3. 解析 2. 类加载器2.1. 类加载器分为四种:前三种为虚拟机自带的加载器。2.2. 类加载有三种方式:2.3. **JVM类加载机制**2.4. 双亲委派…