shiro反复调用RedisSessionDAO.doReadSession造成页面打开缓慢

最近接手了一个项目,跑起来后,发现打开页面的速度特别缓慢,有时仅仅打开一个页面甚至需要几分钟,让人百思不得其解。

观察日志,发现在打开页面时,日志打印及其缓慢,有时几十秒才打印一行,项目仿佛在做慢动作一样。

于是检查了项目注册的几个handler和filter的代码,也没有发现什么复杂的逻辑会大量消耗性能。正在一筹莫展,突然想到可以用jconsole看看线程的方法调用链。于是打开jconsole,打开页面,找到对应的http-nio-8080-exec-*工作线程。 因为系统像慢动作一样,所以顺利的根据打印的工作线程名找到了正在工作的exec线程。

浏览了一下线程的方法栈,好像也没什么特殊的……等等!这一串的redis调用是什么情况? 直觉告诉我,它们很可疑!

继续往上找,发现对这些redisManager的调用来自一个RedisSessionDAO,从类的命名来看,这是shiro管理session的类。


打开一看,原来是个第三方的jar包,作者好像还是个中国人。它继承了shiro的AbstractSessionDAO,实现了对shiro sessionDAO的redis支持。里面主要有doReadSession、delete、saveSession、update几个方法。对应增删改查几个操作。

于是在方法里打了断点,发现会反复进入doReadSession这个方法,次数高达八十多次。

这个项目由于是异地,受网络影响,redis的速度很慢。如果每次读一个session要1秒的话,八十多次就接近一分半了!所以这便是页面打开速度缓慢的原因了。

既然找到了原因当然就要解决了,为了减少网络导致的延迟,最好的方法自然是加缓存了。由于redis在异地,我想到了ehcache这个内存缓存神器。浏览了一下shiro的代码,发现AbstractSession正好有个CachingSessionDAO的抽象子类,它实现了CaCheManagerAware接口。它的read和update方法长的是这样的:


也就是说读的时候会先读cache,读不到再去调实现类的doReadSession方法;更新的时候则会清掉cache。所以只要继承这个类,并重写doReadSession和doUpdate等几个方法就好了。


于是编写LocalRedisSessionDAO:


RedisSessionDAO就是原先配置的SessionDAO。这样整个流程只是加了一层缓存,完全没动原先的逻辑。


当然,ehcache缓存管理器是要配置的:

在spring-cache.xml的nativeEhCacheManger配置里把shared属性设置为true:

<bean id="nativeEhCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="classpath:ehcache.xml" />
    <property name="shared" value="true"></property>
</bean>

在ehcache.xml新增一个cache:

<!-- shiro session的缓存 -->
<cache name="shiroSessionCache"
       maxEntriesLocalHeap="5000"
       eternal="false"
       timeToIdleSeconds="60"
       timeToLiveSeconds="60"
       overflowToDisk="true"
       statistics="true">
</cache>

在spring-shiro.xml加上:

<!-- 缓存管理器 使用Ehcache实现 -->
<bean id="shiroEhCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManager" ref="nativeEhCacheManager" />
</bean>
<bean id="localSessionDAO" class="com.lianjia.estuary.web.LocalRedisSessionDAO">
    <property name="redisSessionDAO" ref="redisSessionDAO"></property>  (给localSessionDAO注入原先redisSessionDAO的bean)
    <property name="cacheManager" ref="shiroEhCacheManager"></property> 
    <property name="activeSessionsCacheName" value="shiroSessionCache"></property> (在这里指定ehCache的缓存名)
</bean>

再把sessionManager里配置的sessionDAO改为localSessionDAO。大工告成!


重新启动服务,页面速度快了很多。 “只”花了几秒钟。

注意:WebSessionManager里的setCacheManager方法会在调用时把sessionDAO里的cacheManager覆盖。 所以如果WebSessionManager有配置cacheManager,需同步改为和sessionDAO里的ehCacheManager。


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值