我有一个Web服务,平均有~1k个请求线程同时运行.这些线程从缓存(当前在ehcache上)访问数据.当缓存中的条目到期时,命中过期条目的线程尝试从DB获取新值,而其他线程也尝试命中此条目块,即我使用BlockingEhCache装饰器.我希望其他线程使用与“遗漏”键对应的“陈旧”值,而不是让其他线程等待“获取线程”.是否有任何第三方为此目的开发了ehcache装饰器?您知道有任何其他具有此行为的缓存解决方案吗?其他建议?
解决方法:
我不知道EHCache是否足以提供解决问题的具体建议,所以我将概述我会做什么,没有EHCache.
假设所有线程都使用名为FooService的Service接口和名为SimpleFooService的服务bean访问此缓存.该服务将具有获取所需数据所需的方法(也被缓存).这样你就可以隐藏它从前端缓存的事实(http请求对象).
我们不是简单地将要缓存的数据存储在服务的属性中,而是为它创建一个特殊对象.我们称之为FooCacheManager.它会将缓存存储在FooCacheManger的一个属性中(比如它的Map类型).它将有getter来获取缓存.它还有一个名为reload()的特殊方法,它将从DB加载数据(通过调用服务方法来获取数据,或通过DAO),并替换缓存的内容(保存在属性中) .
这里的诀窍如下:
>将FooCacheManger中的缓存属性声明为AtomicReference(在Java 1.5中声明的新对象).这可以在您阅读并分配线程时保证线程安全.您的读/写操作永远不会发生冲突,或读取半写值.
> reload()将首先将数据加载到临时映射中,然后在完成后将新映射分配给保存在FooCacheManager中的属性.由于属性是AtomicReference,因此赋值是原子的,因此它基本上是在瞬间刷地图而不需要锁定.
> TTL实现 – 让FooCacheManager实现QuartzJob接口,并使其成为一个石英作业.在作业的execute方法中,让它运行reload().在Spring XML中,定义此作业每隔xx分钟运行一次(您的TTL),如果使用PropertyPlaceHolderConfigurer,也可以在属性文件中定义.
这个方法有效,因为阅读线程:
>不要阻止阅读
>不要在每次读取时调用isExpired(),即1k /秒.
写入线程在写入数据时也不会阻塞.
如果不清楚,我可以添加示例代码.