java shiro 访问频率_Shiro性能优化:解决Session频繁读写问题
目录
背景
应对思路
本地缓存 Session
避免不必要的 Session 更新
代码实现
ShiroSessionDAO.java
ShiroConfig.java
背景
Shiro 提供了强大的 Session 管理功能,基于 Shiro 实现 Session 共享非常方便,只需要定制一个我们自己的SessionDAO,并将它绑定给 SessionManager 即可。在我们的 SessionDAO 中,通常会将 Session 保存到 Redis,那么 Shiro 对 Session 的增删改查,都会直接操作 Redis。
但是由于 Shiro 对 Session 的访问非常频繁,用户的一次请求,可能就会触发几十次的 Session 访问操作,在 Session 共享的场景下,如果每次都访问 Redis,势必会影响性能。
应对思路
本地缓存 Session
将 Session 对象缓存于本地内存中,能够有效减少从 Redis 中读取 Session 的次数。
最简单的方案,就是将 Session 对象保存到 request 域中,那么在一次请求内,只需要从 Redis 中获取一次,之后就可以直接从当前 request 域中获取,并且当请求结束后缓存会自动销毁,不用担心内存泄漏。
避免不必要的 Session 更新
ShiroFilter 对每个请求都会检查 Session 是否存在,如果存在,则调用 SessionManager 的 touch() 方法,将 Session 的 lastAccessTime 属性值更新为当前时间,并调用 SessionDAO 的 update() 方法保存更新。
由此可见,当 Session 被创建出来之后,用户的每个请求都会使 SessionDAO 的 update() 方法至少被调用一次。
那么 Session 的 lastAccessTime 属性是干嘛用的呢?有必要每个请求都去更新一下吗?
lastAccessTime 属性记录的是用户的上次访问时间,它主要用于验证 Session 是否超时,当用户访问系统时,如果本次访问的时间距离上次访问时间超过了 timeout 阈值,则判定 Session 超时。如果 lastAccessTime 的值不断更新,那么 Session 就有可能永不超时。因此,更新 lastAccessTime 属性值的操作可以认为是给 Session “续命”。
既然是“续命”,没必要每次都“续”(除非命真的很短)。我们可以重写 SessionManager 的 touch() 方法,在更新过 lastAccessTime 属性的值后,先不急着保存更新,而是计算一下两次访问的时间间隔,只有当它大于某个阈值时,才去主动调用 SessionDAO 的 update() 方法来保存更新。这样也就大大降低了 Session 更新的频率。
代码实现
ShiroSessionDAO.java
@Repository
public class ShiroSessionDAO extends AbstractSessionDAO {
private static final String SESSION_REDIS_KEY_PREFIX = "session:";
@Autowired
private RedisTemplate redisTemplate;
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX + session.getId().toString()).set(session);
return sessionId;
}
@Override
public void update(Session session) throws UnknownSessionException {
redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX + session.getId().toString()).set(session);
}
@Override
public void delete(Session session) {
redisTemplate.delete(SESSION_REDIS_KEY_PREFIX + session.getId().toString());
HttpServletRequest request = getRequest();
if (request != null) { // 一定要进行空值判断,因为SessionValidationScheduler的线程也会调用这个方法,而在那个线程中是不存在Request对象的
request.removeAttribute(session.getId().toString());
}
}
@Override
protected Session doReadSession(Serializable sessionId) {
HttpServletRequest request = getRequest();
if (request != null) {
Session sessionObj = (Session) request.getAttribute(sessionId.toString());
if (sessionObj != null) {
return sessionObj;
}
}
Session session = (Session) redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX + sessionId).get();
if (session != null && request != null) {
request.setAttribute(sessionId.toString(), session);
}
return session;
}
@Override
public Collection getActiveSessions() {
Set keys = redisTemplate.keys(SESSION_REDIS_KEY_PREFIX + "*");
if (keys != null && !keys.isEmpty()) {
List sessions = redisTemplate.opsForValue().multiGet(keys);
if (sessions != null) {
return sessions.stream().map(o -> (Session) o).collect(Collectors.toList());
}
}
return Collections.emptySet();
}
private HttpServletRequest getRequest() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return requestAttributes != null ? requestAttributes.getRequest() : null;
}
}
ShiroConfig.java
@Configuration
public class ShiroConfig {
@Bean
public SessionManager sessionManager(SessionDAO sessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager() {
@Override // 重写touch()方法,降低Session更新的频率
public void touch(SessionKey key) throws InvalidSessionException {
Session session = doGetSession(key);
if (session != null) {
long oldTime = session.getLastAccessTime().getTime();
session.touch(); // 更新访问时间
long newTime = session.getLastAccessTime().getTime();
if (newTime - oldTime > 300000) { // 如果两次访问的时间间隔大于5分钟,主动持久化Session
onChange(session);
}
}
}
};
sessionManager.setSessionDAO(sessionDAO); // 绑定SessionDAO
SimpleCookie sessionIdCookie = new SimpleCookie("sessionId");
sessionIdCookie.setPath("/");
sessionIdCookie.setMaxAge(8 * 60 * 60); // 单位:秒数
sessionManager.setSessionIdCookie(sessionIdCookie); // 绑定Cookie模版
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionValidationInterval(2 * 60 * 60 * 1000);
sessionManager.setDeleteInvalidSessions(true);
return sessionManager;
}
... 略 ...
}
java shiro 访问频率_Shiro性能优化:解决Session频繁读写问题相关推荐
- redis session 超时时间_Shiro性能优化:解决Session频繁读写问题
点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 作者 | 张永恒 来源 | urlify.cn/YjEZNj 背景 Shiro 提供了强大的 Session 管理 ...
- java shiro 访问频率_java shiro配置记住密码功能 RememberMe
一般来讲,记住密码的基本处理,就是把用户的一些基本信息(密码)存入浏览器的Cookie,下次登录的时候优先验证Cookie,后端做处理:以此来实现记住密码的功能!使用shiro自带的RememberM ...
- java split()方法_Java编程性能优化一些事儿
点击上方 "程序员小乐"关注, 星标或置顶一起成长 每天凌晨00点00分, 第一时间与你相约 每日英文 Smile and stop complaining about the t ...
- 【读书笔记】实战JAVA虚拟机JVM故障诊断与性能优化 读书笔记
文章目录 1.概述 1.1 **第一章:初探java虚拟机** 1.2 认识java虚拟机的基本结构 1.3 常用Java虚拟机参数 1.4 垃圾回收器 1.5 垃圾收集器以及内存分配 1.6 性能监 ...
- 实战java虚拟机 百度云_《实战JAVA虚拟机 JVM故障诊断与性能优化》pdf百度云下载...
内容简介· · · · · · 随着越来越多的第三方语言(Groovy.Scala.JRuby等)在Java虚拟机上运行,Java也俨然成为了一个充满活力的生态圈.<实战Java虚拟机--JVM ...
- 【Java架构师】JVM性能优化(一)JVM技术入门下
JVM性能和"一次编译,到处运行"的挑战 我有新的消息告诉那些固执的认为Java平台本质上是缓慢的人.当Java刚刚做为企业级应用的时候,JVM被诟病的Java性能问题已经是十几年 ...
- vue dve环境static无法被外部访问_vue项目性能优化(代码层面)
点击上方蓝字关注我哦1v-if与v-show区分使用场景 v-if是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建:也是惰性的:如果在初始渲染时条件为假,则什么 ...
- 【ethtool】ethtool 网卡诊断、调整工具、网卡性能优化| 解决丢包严重
目录 即看即用 详细信息 软件简介 安装 ethtool的使用 输出详解 其他指令 将 ethtool 设置永久保存 如何使用 ethtool 优化 Linux 虚拟机网卡性能 ethtool 解决网 ...
- java 大厂面试指南:性能优化 + 微服务 + 并发编程 + 开源框架 + 分布式
秋招面试,我相信有人欢喜有人愁,大厂的面试题千奇百怪,不知道他会问到哪方面的知识点,我也是秋招大部队里面的一员,给大家整理出了 18 个大厂经常会问到 200 多道面试问题,涉及的知识点有,性能优化, ...
最新文章
- easyui 页面加载闪现问题 解决方法
- 数据结构:链表面试题
- 成功解决ERROR: Could not install packages due to an EnvironmentError: [WinError 5] 拒绝访问。backend_agg.cp36
- 计算机卡在无法显示网页,我的电脑上网上银行一直“无法显示网页”
- CSS3属性box-shadow使用教程
- 机器学习笔记(二十)——求解最大熵模型
- 学习django笔记(天天生鲜)
- 西门子200PLC步进控制(入门)
- Python天天美味(15) - Python正则表达式操作指南(re使用)(转)
- springboot 集成quartz带数据库持久化
- 微信第三方平台服务器,EasyWeChat微信开放平台第三方平台接入
- 系统的延时任务和定时任务
- 嵌入式方向如何转行?
- 【k.11】python+appium+雷电模拟器 app自动化测试 demo 教学
- JAVA、PHP统一社会信用代码、身份证号算法解析验证
- [编程神域 C语言浮游塔 第①期] Hello C language world
- 极智开发 | 阿里云ECS本地开发环境搭建
- 1.稀缺数组java实现
- org.springframework.scheduling.quartz.CronTriggerBean 配置
- 实模式、保护模式和虚拟8086模式