使用JCS在Web门户应用中实现对象缓存(1) 使用JCS在Web门户应用中实现对象缓存 在我最近的web门户应用开发工作中,我们需要在Servlet容器(Tomcat)的内存中存储一些查找数据(例如:比率更新数据、状态和产品列表),这样我们不需要在每次访问数据的时候进行数据库查找。同时,我们也需要定期地刷新存储在内存中的数据以保证其新鲜和准确。我们也需要一种机制在不同的时间间隔对存储在内存中的不同类型的数据进行刷新。例如,比率更新数据需要每天刷新一次,而查找类型的数据则可以在内存中保留很长一段时间。对象缓存是最方便地达到上述所有目的的完美解决方案。 对象缓存 最恰当的对象缓存的定义可以在Object Caching Service for Java (OCS4J)的功能规范文档中找到 ,它是这样定义的: 服务器必须将信息和可执行对象分为三种基本类别管理: 永远不会改变的对象,每次请求都会发生改变的对象,以及在两者之间的对象。Java对前两种对象的处理提供了很好的支持,但是对第三种类别的支持非常有限。如果对象永远不改变,我们在服务器初始化的时候创建静态对象。如果对每个请求对象都是不同的,我们每次都创建新的对象。对于介于两者之间的对象或信息,它们的状态可能会发生变化,但是又需要提供跨越请求、跨越用户或跨越进程的共享,就要采用“对象缓存服务”了。 基本上,对象缓存就是通过在使用对象后不立即释放,而是存储在内存中并被后来的客户端请求重用,避免重新获得对象的昂贵成本。当数据第一次从数据源被检索出来后,在名为cache的内存缓冲中临时存放。 同样的数据再次被访问的时候,对象从缓存中取出来,而不是从数据源重新获取。最后,被缓存的数据在不再需要的时候从内存中释放出来。从Web应用的观点来看,什么时候指定的对象可以从内存中释放出来取决于定义一个对象变成无效的合理的间隔时间。 缓存的益处 对象缓存最主要的益处就是对应用性能的重大提升。在一个多层应用中,与其他工作比较,数据访问是一种代价非常高的操作。通过将频繁被访问的数据By keeping the frequently accessed data around并在第一次使用后不释放,我们可以避免重新获取与释放对象的开销和时间。这将会大规模提升web应用的性能,因为我们不用在每次获取数据的时候访问数据库了。 可伸缩性是使用对象缓存带来的另一个好处。因为被缓存的数据可以跨越多个session和web应用 访问,对象缓存可以成为可伸缩性Web应用设计的一个重要部分。采用对象缓存,就像对象池一样,可以帮助避免获取与释放对象的开销。 缓存的不利因素 在web应用中使用对象缓存可以带来很多益处,但是缓存对象同时也会带来一些不利的因素。 同步复杂性: 复杂性的增长依赖于数据的种类,因为需要保证被缓存数据与数据源中原始数据的一致性。要不然,被缓存的数据就会与实际数据不一致,将会导致应用中的数据错误。 Durability: 当服务器崩溃的时候,被缓存数据的修改回丢失。可以采用同步缓存避免该问题。 内存大小: 如果有大量没有用处的数据存放在缓存中,并且这些数据没有在正常的时间间隔内被释放,JVM内存大小就会变得无法接受的庞大。 缓存实例 任何不经常变化的、需要花费很长时间从数据源获取的数据都是缓存的最佳选择。这包括了几乎所有的静态数据、代码和描述列表,以及带有分页功能的查找结果 (搜索结果可以从数据源一次获取出来并存放在缓存中,等待用户在结果页面上点击页数链接时显示)。 中间件技术如EJB和CORBA允许在客户端与服务器之间的远程对象传输。像这种类型的访问,也被称为 粗粒度数据访问,用于降低昂贵的远程方法调用开销。如果这些数据传输对象 (也被称为值对象)变化不是非常频繁,它们可以被存储在缓存中。这样可以在每次客户端需要值对象时减少servlet容器访问应用服务器的次数。 那么哪些类型的数据不宜被缓存呢?这里是一些不推荐使用缓存的数据类别: 可以被网站上其他用户访问的安全信息。 用户描述信息。 私人信息,包括社会保险号码或信用卡明细信息。 经常变化的,并且不准确或不及时会引发问题的业务信息。 不应该被其它用户访问的特定于Session的数据。 使用JCS在Web门户应用中实现对象缓存(2) 缓存架构 有很多种对象缓存架构(包括开放源代码的和商业的实现)在servlet容器和应用服务器中提供了分布式缓存。下面列出了一些现在可以使用的缓存框架: 开放源代码项目 来自于Jakarta的Java Caching System (JCS) (属于Turbine项目的一部分) OSCache Commons Collections (另一个Jakarta项目) JCache API (SourceForge.net) 商业项目 SpiritCache (来自于 SpiritSoft). Coherence (Tangosol) Javlin (eXcelon) Object Caching Service for Java (Oracle) 如果你还有兴趣了解更多的缓存实现,本文最后的资源一节提供了所有这些框架的URL。 我在最初开始我对能满足我们所有的缓存需求的框架进行研究的时候,我先看到了Commons Collections和 Java Caching System API。我在最开始评估的主要标准就是容易使用、容易扩展,以及软件的成本。这两个框架都是开放源代码的,并且都来自于Apache Jakarta项目。 Commons Collections Commons Collections API以一个Java类的形式提供了对象缓存算法,名称为
LRUMap。如果你对缓存只有基本的需求,并且不是真正需要很多的扩展和缓存配置功能,那么Commons Collections可能是一个不坏的选择,但不是作为一个企业级缓存系统的正确选择。如果你选择Common Collections来实现对象缓存,你将会手工定义/配置所有的缓存参数,如:最大缓存对象的数量,一个缓存对象的最长生命周期,刷新缓存或检查对象新鲜的中断时间等。 Java Caching System Java Caching System (JCS), Jakarta Turbine项目的一部分, 与Commons Collections相比更有成熟、更具扩展性。通过为频繁访问的对象维护动态对象池的方法,它提供了提升整个系统性能的灵活、可配置的解决方案。就像在它的网站上提到的,JCS已经超越了简单地在内存中缓存对象的范围。它为一个企业级的缓存系统提供了许多必需的重要功能: 内存管理 Disk overflow (和重整) 元素分组 快速嵌套地移除 数据期限管理 可扩展的框架 完全可配置的运行时参数 远程同步 远程存储恢复 通过HTTP、TCP或者UDP协议实现可选的横向分布元素。 JCS提供了无故障点的框架, 允许完全的session失败转移(在集群环境中),包括支持最多到256个服务器的session数据分布。它也提供了配置一个或多个数据存储选项的灵活性,例如:内存缓存、磁盘缓存,或者在远程机器上的缓存。 所有这些包括在JCS中的优秀功能让它成为满足我们的web门户项目的一个优秀的对象缓存产品选择 使用JCS构造Web门户缓存框架 缓存框架的目标 在我开始设计对象缓存框架之前,我首先列出了在新的框架中需要达到的目标。以下就是目标的列表: 在web门户应用中快速访问频繁使用的数据。 将缓存中类型类似的对象进行组合。 提供可配置的缓存管理,以便我可以通过描述方式而不是编码方式修改参数。 提供可靠的和可扩展的框架,以便我将来可以转换到其它第三方的缓存API。 可以生成统计数据来监测缓存的效率,以及使用数据缓存后应用性能的提升效果。 安装和配置 在web应用中安装和配置JCS是非常简单的事情。从Jakarta Turbine网站下载
压缩文件,解压缩文件到临时目录,并拷贝JSC.jar文件(jcs-1.0-dev.jar)到servlet容器的通用目录(在我的web应用中使用的servlet容器是Tomcat,通用目录在windows下就是%TOMCAT_HOME%\common\lib,在再Unix类型的系统下就是$TOMCAT_HOME/common/lib)。你可能还需要commons-collections.jar, commons-lang.jar,和 commons-logging.jar 这些
文件存在于web应用的类路径下以便使用JCS。 对象缓存框架的主要原理在图1和图2的UML图中展示出来了 图1 图2 使用JCS在Web门户应用中实现对象缓存(3) 框架的主要原理 缓存属性 我们将所有的缓存参数配置在名为cache.ccf的属性文件中。这些参数包括缓存信息如:内存中存储的对象的最大数量,缓存时间(过了时间之后缓存的数据九自动从内存中释放),中断时间(elapsed time since last access time), 内存缓存名称(例如:缓存算法如LRU或MRU)等。在当前版本的JCS中,缓存属性文件是纯文本格式的。SpiritCache framework,一种来自SpiritSoft的Jcache API商业实现,支持XML格式的缓存配置。 确认该属性文件存放在类路径中。注意:如果你需要使用其它不同的文件来存放缓存属性的话,JCS也提供了方法来指定一个配置文件的名称。请参考JCS的 Javadocs学习如何从其它非缺省的属性文件中读取缓存配置信息。 下面列出来的是web应用使用缓存功能需要了解的一些Java类。这些类存放在本文的示例代码的common.caching包中。这些类的Javadocs也包括在源代码压缩包中。 (图2中的类图显示了这些Java类的关系) ICacheManager 这是客户应用实现所有缓存有关操作(如:存储、访问以及释放缓存中的数据)的主接口(契约)。客户程序可以是JSP、Struts Action类,或者就是一个POJO对象。创建该接口用于对客户端隐藏所有缓存的实现细节,这样当我们将来需要切换另一种的第三方缓存API的时候无需对客户端代码做任何调整。 BaseCacheManager
这是web门户缓存框架的主类。是对ICacheManager 接口的最基本实现。创建BaseCacheManager 用于在一个类中集中所有缓存相关的方法。它被设计为单例模式保证在servlet容器的JVM中有且仅有一个ICacheManager 的实例被创建。在多web服务器/servlet容器实例共同处理web请求的集群环境中,每个JVM将会创建独立的ICacheManager 实例。如果将来你要转换到不同的