同一个应用可以实例化多个oscache的admin,使用不同的配置策略,如不同的配置文件。例如:
Public class ChatRecordCache {
private static GeneralCacheAdministrator admin;
private static String OSCACHE_DISK = "oscache_disk.properties";
static {
Properties prop = new Properties();
try {
prop.load(new FileInputStream(OSCACHE_DISK));
} catch (FileNotFoundException e) {
// e.printStackTrace();
} catch (IOException e) {
// e.printStackTrace();
}
admin = new GeneralCacheAdministrator(prop);
}
}
对应的配置文件为oscache_disk.properties:
cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener
cache.path=/opt/myapp/cache
cache.algorithm=com.opensymphony.oscache.base.algorithm.FIFOCache
cache.capacity=1000
cache.unlimited.disk=true
public class ChatUserCache {
private static GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
}
此处采用默认的配置文件oscache.properties.
注意:如果多个cache实例产生的硬盘缓存路径和缓存名称完全相同,将会无法区分,此时要注意设置不同的key值区分。
关于NeedsRefreshException,引用官方文档中的一段例子如下:
// ---------------------------------------------------------------
// Typical use with fail over
// 采取补救措施的典型方案
// ---------------------------------------------------------------
String myKey = "myKey";
String myValue;
int myRefreshPeriod = 1000;
try {
// Get from the cache
myValue = (String) admin.getFromCache(myKey, myRefreshPeriod);
} catch (NeedsRefreshException nre) {
//This exception is thrown when retrieving an item from cache and it //is expired. Note that for fault tolerance purposes, it is possible //to retrieve the current cached object from the exception.
try {
// Get the value (probably by calling an EJB)
myValue = "This is the content retrieved.";
// Store in the cache
admin.putInCache(myKey, myValue);//有解锁的作用
} catch (Exception ex) {//获取myValue过程发生异常,无法执行到putInCache方法
// We have the current content if we want fail-over.
//采取补救措施,获取可能的值(适用于数据过期的情况)
myValue = (String) nre.getCacheContent();
// It is essential that cancelUpdate is called if the
// cached content is not rebuilt
admin.cancelUpdate(myKey);//解锁
}
}
这段代码是使用oscache的经典代码,细说如下。myValue = (String) admin.getFromCache(myKey, myRefreshPeriod);从对象缓存map中取出myKey对应的对象,两种情况可能发生: 一:如果myKey对应的对象存在(先前putInCache)并且没有过期(isStale()方法)那么getFromCache方法会正常返回。二:如果对应的对象不存在或者过期,分为两种情况:1)请求的线程第一个探测到对象不存在或者过期,那么这个时候oscache会抛出一个NeedRefreshException, 提示需要对数据进行一下刷新,怎么刷新,putInCache或者cancelUpdate即可。2)如果请求的线程并非第一个探测到对象不存在,两种情况:A.之前探测到的线程没有进行刷新处理,直接阻塞。B. 之前探测到的线程进行了刷新处理,抛出NeedRefreshException。3)如果请求的线程并非第一个探测到对象过期,两种情况:A.之前探测到的线程没有进行刷新处理,直接阻塞。B. 之前探测到的线程进行了刷新处理,又分两种情况:I.如果oscache.properties中对blocking设置为false(默认cache.blocking=false), 抛出NeedRefreshException,此处采取补救措施nre.getCacheContent(),会取出现有(很可能过期)内容。 II. 如果cache.blocking=true那么该线程会在此阻塞,直到putInCache在另一个线程中被调用或者是cancelUpdate被调用。参考以下说明:
cache.blocking
When a request is made for a stale cache entry, it is possible that another thread is already in the process of rebuilding that entry. This setting specifies how OSCache handles the subsequent 'non-building' threads. The default behaviour (cache.blocking=false) is to serve the old content to subsequent threads until the cache entry has been updated. This provides the best performance (at the cost of serving slightly stale data). When blocking is enabled, threads will instead block until the new cache entry is ready to be served. Once the new entry is put in the cache the blocked threads will be restarted and given the new entry.
Note that even if blocking is disabled, when there is no stale data available to be served threads will block until the data is added to the cache by the thread that is responsible for building the data.
补充:oscache对一个cacheEntry是否是Stale(或者说expire)的判断原则:
1.Cache.flushEntry()
2.Cache.flushAll()
3.CacheEntry.setGroups(groups); Cache.flushGroup(group)
4.createTime + refreshPeriod< now
5.cronExpiry< now
6.CacheEntry自带的EntryRefreshPolish的needRefresh方法返回true
上面的6条中的任何一条如果为true, 那么CacheEntry就是stale的,need refresh!
官方文档中的另一段例子如下:
// ---------------------------------------------------------------
// Typical use without fail over
//不采取补救措施的典型方案
// ---------------------------------------------------------------
String myKey = "myKey";
String myValue;
int myRefreshPeriod = 1000;
try {
// Get from the cache
myValue = (String) admin.getFromCache(myKey, myRefreshPeriod);
} catch (NeedsRefreshException nre) {
try {
// Get the value (probably by calling an EJB)
myValue = "This is the content retrieved.";
// Store in the cache
admin.putInCache(myKey, myValue);
updated = true;
} finally {
if (!updated) {
// It is essential that cancelUpdate is called if the
// cached content could not be rebuilt
admin.cancelUpdate(myKey);
}
}
}
// ---------------------------------------------------------------