`
longzhun
  • 浏览: 370230 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

OSCache 缓存重建在 Race Condition 下的 NRE 问题

阅读更多

OSCache 缓存重建在 Race Condition 下的 NRE 问题

 

一,现象:

高并发情况下,使用 OSCache 作为本地缓存中间件的前端服务,日志文件中会出现大量如下错误信息:

异常堆栈:

java.lang.IllegalStateException:   Cannot complete cache update   -   current state (2) is not UPDATE_IN_PROGRESS

         at com.opensymphony.oscache.base.EntryUpdateState.completeUpdate(EntryUpdateState.java:105)

         at com.opensymphony.oscache.base.Cache. completeUpdate (Cache.java:762)

         at com.opensymphony.oscache.base.Cache. putInCache (Cache.java:619)

         at com.opensymphony.oscache.base.Cache.putInCache(Cache.java:580)

         at com.opensymphony.oscache.general.GeneralCacheAdministrator.putInCache(GeneralCacheAdministrator.java:249)

         at com.opensymphony.oscache.general.GeneralCacheAdministrator.putInCache(GeneralCacheAdministrator.java:259)

 

二,NRE 背景:

无论你使用哪一种本地缓存中间件,如果你缓存数据片段时设置了过期时间,都需要考虑缓存失效后的缓存重建(repopulate the cache )场景。

进一步必须考虑 Race Condition (同进程下多线程,或不同进程)下如何重建。

 

也就是说,某个线程在重建缓存过程中,其他线程发现缓存不存在或已过期,该如何处置?

 

2005OSCache 的版本到现在,它一直这么声称:

    * @throws   NeedsRefreshException   Thrown when the object either

     * doesn't exist, or exists but is stale.   When this exception occurs,

     * the CacheEntry corresponding to the supplied key will be locked

     *   and other threads requesting this entry will potentially be blocked

     *   until the caller repopulates the cache . If the caller choses not

     * to repopulate the cache, they <em>must</em> instead call

     * {@link #cancelUpdate(String)}.

即,

key 不存在,或者存在但数据过期(stale )时,

调用 getFromCache 函数时会抛出 NRE 异常;

当异常发生时,

对应于这个 key CacheEntry 将被锁住,

而请求这个 entry 的其他线程将可能(注意,仅仅是可能) 被阻塞,直到调用者重建缓存。

如果调用者没有选择重建缓存,必须   调用 cancelUpdate 函数来“Cancels any pending update for this cache entry ”。

 

三,缓存重建的 OSCache 官方推荐做法:

读取缓存遇到 NRE 异常时,OSCache 官方推荐的做法是:

 1 String myKey = "myKey";

 2 String myValue;

 3 int myRefreshPeriod = 1000;

 4 try {

 5      // Get from the cache

 6      myValue = (String) admin. getFromCache (myKey, myRefreshPeriod);

 7 } catch ( NeedsRefreshException nre) {

 8      try {

 9          // Get the value (probably from the database)

10          myValue = "This is the content retrieved.";

11          // Store in the cache

12          admin. putInCache (myKey, myValue);

13      } catch (Exception ex) {

14          // We have the current content if we want fail-over.

15          myValue = (String) nre.getCacheContent();

16          // It is essential that cancelUpdate is called if the

17          // cached content is not rebuilt

18          admin. cancelUpdate (myKey);

19      }

20 }

即,本线程先试图重建缓存,如果再次发生异常,则本线程(不管三七二十一)直接调用 cancelUpdate 函数。

现在的一些 OSCache Manager 工具类, get 方法也就实现为一旦捕获 NRE 异常就直接 canelUpdate

1 public OSCache get(String key, int myRefreshPeriod){ 

2       try

3           return (OSCache) this .admin. getFromCache (key,myRefreshPeriod); 

4       } catch ( NeedsRefreshException ex){ 

5           this .admin. cancelUpdate (key); 

6           return null

7       } 

8   }  

 

四,OSCache Race Condition 下缓存重建的特殊场景

简单地说,就是:

线程1 正在重建缓存;

线程2 读取缓存时得到 NRE 异常,主动 cancel update

线程1 重建缓存完毕,却发现状态被改为了 UPDATE_CANCELLED ,与期望不符,于是抛出异常 java.lang.IllegalStateException 

 

具体过程如下:

0 )缓存过期;

1 )线程 T1 获得 update lock ,并开始调用 putInCache 函数 

2 线程 T2 T1 没有结束 update 之前,也开始 getCacheEntry   了;

3T2 调用的 getCacheEntry 函数捕获  NeedsRefreshException  异常;

4T2 调用 cancelUpdate 函数来“取消所有试图更新本 cache entry 的操作”, 于是 EntryUpdateState 变为 UPDATE_CANCELLED ,它是一个正整数 2

5T1 其实已经重建了缓存

6T1 随后调用 completeUpdateEntryUpdateState.java,93 行) 来通知那些等着本次更新操作的线程;  completeUpdate 函数却发现当前 EntryUpdateState  居然不等于 UPDATE_IN_PROGRESS (对应0 ),而是 UPDATE_CANCELLED (对应2 ),于是抛出异常 。即下面代码抛出的 IllegalStateException 异常,文字通常为:“ Cannot complete cache update - current state ( 2 ) is not UPDATE_IN_PROGRESS ”,其中的 2 就是指  UPDATE_CANCELLED

1.     /**  

2.      * Updates the state to <code>UPDATE_COMPLETE</code>. This should <em>only</em>  

3.      * be called by the thread that managed to get the update lock.  

4.      * @return the counter value after the operation completed  

5.      */   

6.     public   int  completeUpdate() {  

7.          if  (state != UPDATE_IN_PROGRESS) {  

8.              throw   new  IllegalStateException( "Cannot complete cache update - current state ("  + state +  ") is not UPDATE_IN_PROGRESS" );  

9.         }  

10.    

11.      state = UPDATE_COMPLETE;  

12.       return  decrementUsageCounter();  

13.  }  

 

总之,按目前 OSCacheManager 的做法,在高并发环境下,一旦一个 OSCache 缓存失效,而缓存的数据片段很大,那么很有可能让其他线程在 getFromCache 时有机会捕获 NRE 异常,最终导致做缓存重建的线程抛出 IllegalStateException 异常,虽然此时缓存已经重建完毕。

分享到:
评论

相关推荐

    OsCache缓存框架使用示例

    OsCache还提供了线程安全的缓存操作,确保在多线程环境下数据的一致性。 在天气预报Web服务示例中,我们可能会频繁地查询某个城市的天气信息。如果每次都去调用外部的Web服务,那么将增加网络延迟和服务器压力。...

    oscache缓存技术

    - **事件监听**:osCache允许注册监听器来处理缓存的添加、删除和更新事件,这在实现缓存同步或记录日志时非常有用。 - **缓存预热**:在应用启动时,可以预先加载一部分常用数据到缓存,提高系统启动后的响应速度...

    oscache缓存技术入门实例

    4. 缓存同步:在多线程或多服务器环境下,osCache提供了一套机制来确保缓存数据的一致性和完整性。 三、osCache配置 osCache的配置主要通过XML文件完成,包括缓存配置、更新策略、缓存监听器等。例如: ```xml ...

    基于OSCache的页面缓存(收藏)

    当用户请求一个页面时,OSCache会先检查该页面是否已经在缓存中,如果存在则直接返回,否则生成页面并将其放入缓存。 要使用OSCache,我们需要在项目的类路径下引入OSCache的jar包,如`oscache.jar`。然后,在初始...

    OSCache缓存技术(6)【实例】

    此外,OSCache还支持集群环境下的缓存同步,通过`ClusteredCache`接口,可以在多台服务器之间共享缓存数据,实现分布式缓存。这在大型分布式系统中非常关键,因为它能确保数据的一致性和可用性。 OSCache的配置主要...

    oscache缓存配置

    除了静态配置文件外,osCache还支持动态编程配置,可以在代码中通过`CacheManager`实例创建和管理缓存。例如: ```java import net.sf.oscache.CacheManager; CacheManager.init("oscache.properties"); // 初始化...

    OSCache缓存框架的简单用法

    OSCache支持分布式缓存,可以在多台服务器之间共享数据。通过配置网络通信和序列化策略,可以实现跨节点的缓存操作。 **7. 过期与清理机制** OSCache支持自动过期,可以设置对象的存活时间和空闲时间。当达到预设...

    Hibernate OSCache缓存

    在使用 Hibernate OSCache 缓存之前,首先需要在项目中引入必要的依赖。将 `oscache.jar` 文件放入 `/WEB-INF/lib` 目录或相应的类库目录。同时,需要一个 `oscache.properties` 配置文件,通常放在项目的 `src` 根...

    OSCache缓存jsp例子

    在本文中,我们将深入探讨OSCache在缓存JSP页面方面的应用,以及如何利用它来优化Web应用。 首先,我们需要理解缓存的基本概念。缓存是一种存储技术,用于临时存储频繁访问的数据,以便快速检索。在Web开发中,JSP...

    OSCache 缓存对象的总结

    4. **缓存同步**:在多线程或多服务器环境下,OSCache 提供了缓存同步机制,确保了在并发操作下的数据一致性。 5. **缓存分区**:OSCache 支持将缓存划分为多个分区,每个分区可以有不同的配置,这有助于管理和优化...

    一个OSCache缓存技术的关键zip包

    - 同步管理:OSCache支持多线程环境下的缓存同步,确保了在并发访问时的数据一致性。 - 自动过期:可以设置缓存项的存活时间和空闲时间,当达到预设时间后自动清理,防止内存溢出。 - 动态更新:当源数据发生变化...

    oscache缓存

    osCache是Java平台上的一个高效的缓存解决方案,主要用于在应用程序中缓存数据,以提高性能和减少数据库的负载。这个工具特别适用于那些需要频繁访问但更新不频繁的数据,例如经常查询但很少更改的数据库记录。...

    oscache缓存使用总结.doc

    OSCache 是一个广泛使用的开源缓存解决方案,尤其在Java应用中,它被JBoss, Hibernate, Spring等知名框架所支持。其主要特点是配置简单,适用于页面级别的缓存管理。以下是对OSCache配置和使用过程的详细说明: 1. ...

    oscache,缓存机制的使用

    标题与描述概述的知识点主要集中在oscache的使用及其在Java环境下的配置与实施。oscache是一种缓存机制,主要用于提高Web应用的响应速度和优化系统性能。以下是对这些知识点的详细解析: ### oscache简介 oscache...

    oscache-java缓存框架

    osCache是Java开发中常用的缓存框架之一,它主要用于提高应用程序的性能和效率,通过将数据存储在内存中,减少对数据库的访问。osCache不仅可以用于Web应用,也可以用于任何Java应用程序,支持集群环境,提供了丰富...

    oscache缓存中间件

    OSCache标记库由OpenSymphony设计,它是一种开创性的缓存方案,它提供了在现有JSP页面之内实现内存缓存的功能。OSCache是个一个被广泛采用的高性能的J2EE缓存框架,OSCache还能应用于任何Java应用程序的普通的缓存...

    用OSCache进行缓存对象

    1、OSCache是什么? 2、OSCache的特点 3、有关“用OSCache进行缓存对象”的研究

Global site tag (gtag.js) - Google Analytics