其实之前我完全没有接触过oscache,今天突发奇想,准备看看缓存是怎么实现的,google了一下,决定看看oscache的源码,简单的写了个TestCase:
@Test
public void testPojoCache() throws Exception {
TestPojo pojo = new TestPojo("0001");
pojo.setField1(100);
pojo.setField2("100");
Properties prop = new Properties();
InputStream is = this.getClass().getResourceAsStream("oscache.properties");
prop.load(is);
is.close();
GeneralCacheAdministrator cacheAdmin = new GeneralCacheAdministrator(prop);
cacheAdmin.putInCache(pojo.getId(), pojo);
TestPojo cachedObj = (TestPojo) cacheAdmin.getFromCache("0001");
assertEquals(100, cachedObj.getField1());
assertEquals("100", cachedObj.getField2());
}
所以我对这个产品的熟悉程度基本为0,各位大虾看了后发现误人子弟请用砖轻拍。
简单了看了下oscache的介绍,发现它支持三种缓存方式:JSP Caching、Request Caching、General-Purpose Cache,今天的文章就是针对最后一种General-Purpose Cache的,在oscache中对应的入口是GeneralCacheAdministrator,就从这里开始吧:
public GeneralCacheAdministrator(Properties p) {
super(p);
log.info("Constructed GeneralCacheAdministrator()");
createCache();
}
// 这个是super(p)调用的构造函数
protected AbstractCacheAdministrator(Properties p) {
loadProps(p);
initCacheParameters();
if (log.isDebugEnabled()) {
log.debug("Constructed AbstractCacheAdministrator()");
}
}
// 初始化参数
private void initCacheParameters() {
algorithmClass = getProperty(CACHE_ALGORITHM_KEY);
blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));
String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);
if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
memoryCaching = false;
}
unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY))
.booleanValue();
String cacheSize = getProperty(CACHE_CAPACITY_KEY);
try {
if ((cacheSize != null) && (cacheSize.length() > 0)) {
cacheCapacity = Integer.parseInt(cacheSize);
}
} catch (NumberFormatException e) {
log.error("The value supplied for the cache capacity, '" + cacheSize
+ "', is not a valid number. The cache capacity setting is being ignored.");
}
}
// 创建缓存实例
private void createCache() {
log.info("Creating new cache");
// 这里构建缓存时用到的参数都是在父类里的#initCacheParameters()中初始化的
applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isOverflowPersistence(),
isBlocking(), algorithmClass, cacheCapacity);
configureStandardListeners(applicationCache);
}
// Cache的构造函数
public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence,
boolean blocking, String algorithmClass, int capacity) {
// 这里说明还是支持自定义的缓存策略的
if (((algorithmClass != null) && (algorithmClass.length() > 0)) && (capacity > 0)) {
try {
cacheMap = (AbstractConcurrentReadCache) Class.forName(algorithmClass).newInstance();
cacheMap.setMaxEntries(capacity);
} catch (Exception e) {
log.error("Invalid class name for cache algorithm class. " + e.toString());
}
}
if (cacheMap == null) {
// 选择一个默认的策略
if (capacity > 0) {// 如果有缓存数目的限制,使用LRU
cacheMap = new LRUCache(capacity);
} else {// 否则使用Unlimited
cacheMap = new UnlimitedCache();
}
}
cacheMap.setUnlimitedDiskCache(unlimitedDiskCache);
cacheMap.setOverflowPersistence(overflowPersistence);
cacheMap.setMemoryCaching(useMemoryCaching);
this.blocking = blocking;
}
// 配置事件的listener
protected Cache configureStandardListeners(Cache cache) {
if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
cache = setPersistenceListener(cache);
}
if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) {
// Grab all the specified listeners and add them to the cache's
// listener list. Note that listeners that implement more than
// one of the event interfaces will be added multiple times.
CacheEventListener[] listeners = getCacheEventListeners();
for (int i = 0; i < listeners.length; i++) {
// Pass through the configuration to those listeners that
// require it
if (listeners[i] instanceof LifecycleAware) {
try {
((LifecycleAware) listeners[i]).initialize(cache, config);
} catch (InitializationException e) {
log.error("Could not initialize listener '" + listeners[i].getClass().getName()
+ "'. Listener ignored.", e);
continue;
}
}
if (listeners[i] instanceof CacheEntryEventListener) {
cache.addCacheEventListener(listeners[i]);
} else if (listeners[i] instanceof CacheMapAccessEventListener) {
cache.addCacheEventListener(listeners[i]);
}
}
}
return cache;
}
这里对缓存#initCacheParameters()和#configureStandardListeners()里的参数大致了解下,代码里有注释良好的JavaDoc,很容易看懂。
1. cache.algorithm 缓存的策略,具体就是用哪种Map来实现缓存,默认有FIFO,LRU,Unlimited三种,也支持自定义的缓存类型的;
2. cache.blocking 是否等待新数据放入缓存,这个感觉应该是在并发读取数据的时候如果数据在其他线程正处于写入的状态这个线程会wait()直到写入线程完成写入后notifyAll();
3. cache.memory 这个是是否使用内存缓存,这个多数情况下都应该是内存的吧;
4. cache.unlimited.disk 看介绍写的是当对象需要持久化缓存(应该是串行化吧)时,是否使用无限的磁盘空间;
5. cache.persistence.overflow.only 这个说明持久化缓存是不是仅在溢出的方式下开启,字面理解应该是否仅在是memory缓存不足的情况开启持久化缓存;
6. cache.capacity 保存的对象的数目。
7. cache.persistence.class 用于持久化的类名
8. cache.event.listeners 缓存事件监听器,多个listener使用逗号分隔开
初始化大致就这么多,还是看看GeneralCacheAdministrator#putInCache()方法吧,这里#putInCache()有4个,我挑了一个参数最多的:putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy)
/**
* Puts an object in a cache
*
* @param key The unique key for this cached object
* @param content The object to store
* @param groups The groups that this object belongs to
* @param policy The refresh policy to use
*/
public void putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy) {
// 直接调用的Cache类的#putInCache
getCache().putInCache(key, content, groups, policy, null);
}
// Cache的#putInCache()方法
public void putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy,
String origin) {
// 首先查找这个key在缓存中是否已经存在,没有就创建一个
CacheEntry cacheEntry = this.getCacheEntry(key, policy, origin);
// 判断是否是新创建的缓存
boolean isNewEntry = cacheEntry.isNew();
// [CACHE-118] If we have an existing entry, create a new CacheEntry so
// we can still access the old one later
// 这里如果不是新的缓存也会新建一个CacheEntry,因为老的缓存值在后边也能访问到
if (!isNewEntry) {
cacheEntry = new CacheEntry(key, policy);
}
cacheEntry.setContent(content);
cacheEntry.setGroups(groups);
// 放入缓存
cacheMap.put(key, cacheEntry);
// Signal to any threads waiting on this update that it's now ready for them in the cache!
// 这里会通知其他在等待值的线程结束wait
completeUpdate(key);
// 针对缓存事件的listener发送事件
if (listenerList.getListenerCount() > 0) {
CacheEntryEvent event = new CacheEntryEvent(this, cacheEntry, origin);
if (isNewEntry) {
dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_ADDED, event);
} else {
dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_UPDATED, event);
}
}
}
先简单的看看CacheEntry的构造函数吧,这个最基本:
public CacheEntry(String key, EntryRefreshPolicy policy, String[] groups) {
// CacheEntry中保存了key,所属的分组(用一个HashSet保存分组的名字,有点像Tag)
// 还有刷新策略和创建的时间
this.key = key;
if (groups != null) {
this.groups = new HashSet(groups.length);
for (int i = 0; i < groups.length; i++) {
this.groups.add(groups[i]);
}
}
this.policy = policy;
this.created = System.currentTimeMillis();
}
另外还有#completeUpdate()方法:
protected void completeUpdate(String key) {
// Entry的更新状态,有NOT_YET_UPDATING, UPDATE_IN_PROGRESS, UPDATE_COMPLETE,
// UPDATE_CANCELLED四种
EntryUpdateState state;
// Cache用一个updateStates(HashMap)来保存各个CacheEntry的更新状态
synchronized (updateStates) {
state = (EntryUpdateState) updateStates.get(key);
if (state != null) {
synchronized (state) {
int usageCounter = state.completeUpdate();
// 通知其他线程结束等待
state.notifyAll();
// 从updateStates移除key
checkEntryStateUpdateUsage(key, state, usageCounter);
}
} else {
// If putInCache() was called directly (i.e. not as a result of
// a NeedRefreshException) then no EntryUpdateState would be
// found.
}
}
}
// EntryUpdateState的#completeUpdate()方法
public int completeUpdate() {
// 状态不正确,在实际的测试中这个异常是可以报出来的
if (state != UPDATE_IN_PROGRESS) {
throw new IllegalStateException("Cannot complete cache update - current state (" + state
+ ") is not UPDATE_IN_PROGRESS");
}
state = UPDATE_COMPLETE;
return decrementUsageCounter();
}
private void checkEntryStateUpdateUsage(String key, EntryUpdateState state, int usageCounter) {
// Clean up the updateStates map to avoid a memory leak once no thread
// is using this EntryUpdateState instance anymore.
// 在这里,如果没有了对这个key的"引用",那么就是updateStates中去掉它
if (usageCounter == 0) {
// 这里的remove操作感觉不光能像原来的注释所说的避免内存泄漏,另外还能对多线程同时put,get有用处
EntryUpdateState removedState = (EntryUpdateState) updateStates.remove(key);
if (state != removedState) {
if (log.isErrorEnabled()) {
try {
throw new Exception("OSCache: internal error: removed state [" + removedState
+ "] from key [" + key + "] whereas we expected [" + state + "]");
} catch (Exception e) {
log.error(e);
}
}
}
}
}
这里要注意一个事情,其实在#getFromCache()是可以并发读取缓存数据的,而写入的时候只能一个线程写入,另外在写入的时候,更新实际的缓存和修改更新状态是独立开的,我在实际的测试中也发现了并发量很高的连续读写操作其实在#completeUpdate()方法中是会抛出异常的,不过在实际使用中这个情况应该发生的较少。
这里,整个存入缓存的大致流程就介绍完了,当处理多线程并发写入读取时,很多情况是要和#getFromCache()一起结合看的。
分享到:
相关推荐
JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源...
- 将`oscache.properties`和`oscache.tld`放入`WEB-INF/classes`(或自动编译到此目录的源代码目录)。 - 配置`oscache.properties`以满足项目需求。 2. **使用方法** - **缓存对象**:通过调用API接口直接缓存...
- **缓存对象**:使用`OsCache.put(key, value)`方法将对象放入缓存,key是唯一标识,value是要缓存的对象。 - **获取缓存**:通过`OsCache.get(key)`方法可以从缓存中获取对象。 - **更新缓存**:如果数据发生...
oscache-2.1.jar oscache-2.1.jar
- **对象缓存**:osCache允许开发者将Java对象直接放入缓存,便于快速检索。 - **内存管理**:自动管理内存,当内存不足时,可基于LRU(Least Recently Used)策略进行对象的淘汰。 - **持久化**:支持缓存数据的...
OsCache是Java应用程序中常用的缓存框架,它能够有效地提高应用程序的性能,通过将经常访问的数据存储在内存中,减少对数据库或其他数据源的访问,从而降低系统负载。本示例将通过一个天气预报Web服务的场景,详细...
OSCache是OpenSymphony开发的一款高效、...总之,OSCache-2.4.1是一个强大的缓存解决方案,旨在提高JSP应用的性能,减轻数据库压力,通过简单易用的JSP标记库和完善的配置选项,使得开发者能快速集成并优化缓存策略。
// 将数据放入缓存 CacheManager.getInstance().put(key, data); } %> <oscache:cache id="myCachedData"> </oscache:cache> ``` 在这个例子中,如果`myCachedData`不在缓存中,会调用`getExpensiveData()`...
在本例中,我们已经有了oscache-2.4.1.jar,这是一个包含osCache核心库的文件。在Java项目中,通常将其添加到项目的类路径(classpath)中,以便程序能够找到并使用osCache的相关类。 接着,我们需要配置osCache的...
- 引入依赖:将oscache-2.0.2-22Jan04.jar添加到项目的类路径中。 - 配置:根据应用需求编辑oscache.properties,设置缓存策略、事件监听器等。 - 初始化:在应用程序启动时初始化OSCache,例如使用`...
网上对于OSCache缓存Web页面很多说明和例子,但对于缓存对象方面说得不多,我就把自已写得一些东西放出来,让大家看一看是怎样缓存对象的! 我基于GeneralCacheAdministrator类来写的BaseCache类 view plaincopy...
当用户请求一个页面时,OSCache会先检查该页面是否已经在缓存中,如果存在则直接返回,否则生成页面并将其放入缓存。 要使用OSCache,我们需要在项目的类路径下引入OSCache的jar包,如`oscache.jar`。然后,在初始...
这两个jar文件,"ajax4jsf-1.1.0.jar" 和 "oscache-2.3.2.jar",在开发过程中扮演着至关重要的角色。 Ajax4JSF,全称为Asynchronous JavaScript and XML for JavaServer Faces,是一个用于扩展JSF功能的开源库。它...
在提供的压缩包中,`oscache-2.4.1.jar`是osCache的二进制库文件,包含了所有必要的类和资源,可以直接引入到Java项目中使用。`docs`目录可能包含osCache的API文档,帮助开发者了解和使用各个类和方法。`src`目录...
在源代码中,可能会看到如何创建、管理缓存实例,设置缓存策略,以及如何将Ehcache集成到Java应用中的示例。 2. Memcached:Memcached 是一个高性能、分布式的内存对象缓存系统,用于减少数据库负载,提升Web应用...
osCache 是一个开源的、基于Java的缓存框架,它为Java应用程序提供了高效且可配置的内存缓存功能。在本文中,我们将深入探讨osCache的基本概念、工作原理以及如何在实际项目中进行配置和使用。 一、osCache基本概念...
本篇文章将深入探讨如何使用注解配置Spring与EHCache或OSCache这两个流行的Java缓存解决方案。以下是对该主题的详细阐述: 1. **Spring缓存抽象** Spring 3.1引入了一种统一的缓存抽象,它允许开发者在不关心具体...
缓存技术 oscache-2.3.2.jar包下载