`

EHCache提升网站性能

阅读更多
使用EHCache提升网站性能
    本文为转载!!
 
这是一篇很早以前写得文章,整理了一下贴上来,希望对不熟悉EHcache的同学有用。
 
    在开发高并发量,高性能的网站应用系统时,缓存Cache起到了非常重要的作用。本文主要介绍EHCache的使用,以及使用EHCache 的实践经验。
    笔者使用过多种基于Java的开源Cache组件,其中包括OSCache、JBossCache、EHCache。OSCache功能强 大,使用灵活,可用于对象缓存、Filter缓存以及在JSP中直接使用cache标签。笔者在最近的使用过程中发现,在并发量较高时,OSCache会 出现线程阻塞和数据错误,通过分析源代码发现是其内部实现的缺陷。JBossCache最大的优点是支持基于对象属性的集群同步,不过 JBossCache的配置使用都较复杂,在并发量较高的情况下,对象属性数据在集群中同步也会加大系统的开销。以上两种Cache本文仅作简单介绍,不 做深入探讨。
    EHCache是来自sourceforge(http://ehcache.sourceforge.net/)的开源项目,也是纯 Java实现的简单、快速的Cache组件。EHCache支持内存和磁盘的缓存,支持LRU、LFU和FIFO多种淘汰算法,支持分布式的Cache, 可以作为Hibernate的缓存插件。同时它也能提供基于Filter的Cache,该Filter可以缓存响应的内容并采用Gzip压缩提高响应速 度。

    EHCache API的基本用法
    首先介绍CacheManager类。它主要负责读取配置文件,默认读取CLASSPATH下的ehcache.xml,根据配置文件创建并 管理Cache对象。
    // 使用默认配置文件创建CacheManager
    CacheManager manager = CacheManager.create();
    // 通过manager可以生成指定名称的Cache对象
    Cache cache = cache = manager.getCache("demoCache");
    // 使用manager移除指定名称的Cache对象
    manager.removeCache("demoCache");
可以通过调用manager.removalAll()来移除所有的Cache。通过调用manager的shutdown()方法可以关闭 CacheManager。
    有了Cache对象之后就可以进行一些基本的Cache操作,例如:
    //往cache中添加元素
Element element = new Element("key", "value");
cache.put(element);
//从cache中取回元素
Element element = cache.get("key");
element.getValue();
//从Cache中移除一个元素
cache.remove("key");
可以直接使用上面的API进行数据对象的缓存,这里需要注意的是对于缓存的对象都是必须可序列化的。在下面的篇幅中笔者还会介绍EHCache和 Spring、Hibernate的整合使用。

    配置文件
    配置文件ehcache.xml中命名为demoCache的缓存配置:
    <cache name="demoCache" 
        maxElementsInMemory="10000" 
        eternal="false"
        overflowToDisk="true" 
        timeToIdleSeconds="300" 
        timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LFU" />
    
各配置参数的含义:
maxElementsInMemory:缓存中允许创建的最大对象数
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值, 这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0 就意味着元素可以停顿无穷长的时间。
overflowToDisk:内存不足时,是否启用磁盘缓存。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。LRU和FIFO算法这里就不做介绍。LFU算法直接淘汰使用 比较少的对象,在内存保留的都是一些经常访问的对象。对于大部分网站项目,该算法比较适用。
如果应用需要配置多个不同命名并采用不同参数的Cache,可以相应修改配置文件,增加需要的Cache配置即可。

    利用Spring APO整合EHCache
    首先,在CLASSPATH下面放置ehcache.xml配置文件。在Spring的配置文件中先添加如下cacheManager配置:
<bean id="cacheManager"
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
</bean>
配置demoCache:
<bean id="demoCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager" ref="cacheManager" />
    <property name="cacheName">
        <value>demoCache</value>
    </property>
</bean>
接下来,写一个实现org.aopalliance.intercept.MethodInterceptor接口的拦截器类。有了拦截器就可以有选择性 的配置想要缓存的 bean 方法。如果被调用的方法配置为可缓存,拦截器将为该方法生成 cache key 并检查该方法返回的结果是否已缓存。如果已缓存,就返回缓存的结果,否则再次执行被拦截的方法,并缓存结果供下次调用。具体代码如下:
public class MethodCacheInterceptor implements MethodInterceptor,
        InitializingBean {

    private Cache cache;

    
    public void setCache(Cache cache) {
        this.cache = cache;
    }

    
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache,
                "A cache is required. Use setCache(Cache) to provide one.");
    }

    
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] arguments = invocation.getArguments();
        Object result;

        String cacheKey = getCacheKey(targetName, methodName, arguments);
        Element element = null;
        synchronized (this){
            element = cache.get(cacheKey);

            if (element == null) {
                //调用实际的方法
                result = invocation.proceed();
                element = new Element(cacheKey, (Serializable) result);
                cache.put(element);
            }
        }
        return element.getValue();
    }

    
    private String getCacheKey(String targetName, String methodName,
            Object[] arguments) {
        StringBuffer sb = new StringBuffer();
        sb.append(targetName).append(".").append(methodName);
        if ((arguments != null) && (arguments.length != 0)) {
            for (int i = 0; i < arguments.length; i++) {
                sb.append(".").append(arguments[i]);
            }
        }
        return sb.toString();
    }
}
synchronized (this)这段代码实现了同步功能。为什么一定要同步?Cache对象本身的get和put操作是同步的。如果我们缓存的数据来自数据库查询,在没有这 段同步代码时,当key不存在或者key对应的对象已经过期时,在多线程并发访问的情况下,许多线程都会重新执行该方法,由于对数据库进行重新查询代价是 比较昂贵的,而在瞬间大量的并发查询,会对数据库服务器造成非常大的压力。所以这里的同步代码是很重要的。

    接下来,继续完成拦截器和Bean的配置:
<bean id="methodCacheInterceptor" class="com.xiebing.utils.interceptor.MethodCacheInterceptor">
    <property name="cache">
        <ref local="demoCache" />

来源:(http://blog.sina.com.cn/s/blog_40c7872a0100h7s1.html?retcode=0 ) - 使用EHCache提升网站性能_编程浪子_新浪博客

    </property>
</bean>
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice">
        <ref local="methodCacheInterceptor" />
    </property>
    <property name="patterns">
        <list>
            <value>.*myMethod</value>
        </list>
    </property>
</bean>
    
<bean id="myServiceBean"
    class="com.xiebing.ehcache.spring.MyServiceBean">
</bean>

<bean id="myService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref local="myServiceBean" />
    </property>
    <property name="interceptorNames">
        <list>
            <value>methodCachePointCut</value>
        </list>
    </property>
</bean>

其中myServiceBean是实现了业务逻辑的Bean,里面的方法myMethod()的返回结果需要被缓存。这样每次对 myServiceBean的myMethod()方法进行调用,都会首先从缓存中查找,其次才会查询数据库。使用AOP的方式极大地提高了系统的灵活 性,通过修改配置文件就可以实现对方法结果的缓存,所有的对Cache的操作都封装在了拦截器的实现中。

    CachingFilter功能
使用Spring的AOP进行整合,可以灵活的对方法的的返回结果对象进行缓存。CachingFilter功能可以对HTTP响应的内容进行缓存。这种 方式缓存数据的粒度比较粗,例如缓存整张页面。它的优点是使用简单、效率高,缺点是不够灵活,可重用程度不高。
    EHCache使用SimplePageCachingFilter类实现Filter缓存。该类继承自CachingFilter,有默认 产生cache key的calculateKey()方法,该方法使用HTTP请求的URI和查询条件来组成key。也可以自己实现一个Filter,同样继承 CachingFilter类,然后覆写calculateKey()方法,生成自定义的key。
在笔者参与的项目中很多页面都使用AJAX,为保证JS请求的数据不被浏览器缓存,每次请求都会带有一个随机数参数i。如果使用 SimplePageCachingFilter,那么每次生成的key都不一样,缓存就没有意义了。这种情况下,我们就会覆写 calculateKey()方法。

要使用SimplePageCachingFilter,首先在配置文件ehcache.xml中,增加下面的配置:
<cache name="SimplePageCachingFilter" maxElementsInMemory="10000" eternal="false"
        overflowToDisk="false" timeToIdleSeconds="300" timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LFU" />
其中name属性必须为SimplePageCachingFilter,修改web.xml文件,增加一个Filter的配置:
<filter>
    <filter-name>SimplePageCachingFilter</filter-name>
    <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SimplePageCachingFilter</filter-name>
    <url-pattern>/test.jsp</url-pattern>
</filter-mapping>

下面我们写一个简单的test.jsp文件进行测试,缓存后的页面每次刷新,在600秒内显示的时间都不会发生变化的。代码如下:
<%
out.println(new Date());
%>

CachingFilter输出的数据会根据浏览器发送的Accept-Encoding头信息进行Gzip压缩。经过笔者测试,Gzip压缩后的数据量 是原来的1/4,速度是原来的4-5倍,所以缓存加上压缩,效果非常明显。
在使用Gzip压缩时,需注意两个问题:
1.    Filter在进行Gzip压缩时,采用系统默认编码,对于使用GBK编码的中文网页来说,需要将操作系统的语言设置 为:zh_CN.GBK,否则会出现乱码的问题。
2.    默认情况下CachingFilter会根据浏览器发送的请求头部所包含的Accept-Encoding参数值来判断是否进行Gzip压 缩。虽然IE6/7浏览器是支持Gzip压缩的,但是在发送请求的时候却不带该参数。为了对IE6/7也能进行Gzip压缩,可以通过继承 CachingFilter,实现自己的Filter,然后在具体的实现中覆写方法acceptsGzipEncoding。

具体实现参考:
protected boolean acceptsGzipEncoding(HttpServletRequest request) {
    final boolean ie6 = headerContains(request, "User-Agent", "MSIE 6.0");
    final boolean ie7 = headerContains(request, "User-Agent", "MSIE 7.0");
    return acceptsEncoding(request, "gzip") || ie6 || ie7;
}

    EHCache在Hibernate中的使用
EHCache可以作为Hibernate的二级缓存使用。在hibernate.cfg.xml中需增加如下设置:
<prop key="hibernate.cache.provider_class">
    org.hibernate.cache.EhCacheProvider
</prop>
然后在Hibernate映射文件的每个需要Cache的Domain中,加入类似如下格式信息:
<cache usage="read-write|nonstrict-read-write|read-only" />
比如:
<cache usage="read-write" />
最后在配置文件ehcache.xml中增加一段cache的配置,其中name为该domain的类名。
<cache name="domain.class.name"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
/>

    EHCache的监控
    对于Cache的使用,除了功能,在实际的系统运营过程中,我们会比较关注每个Cache对象占用的内存大小和Cache的命中率。有了这些 数据,我们就可以对Cache的配置参数和系统的配置参数进行优化,使系统的性能达到最优。EHCache提供了方便的API供我们调用以获取监控数据, 其中主要的方法有:

//得到缓存中的对象数
cache.getSize();
//得到缓存对象占用内存的大小
cache.getMemoryStoreSize();
//得到缓存读取的命中次数
cache.getStatistics().getCacheHits()
//得到缓存读取的错失次数
cache.getStatistics().getCacheMisses()

    分布式缓存
EHCache从1.2版本开始支持分布式缓存。分布式缓存主要解决集群环境中不同的服务器间的数据的同步问题。具体的配置如下:

在配置文件ehcache.xml中加入
<cacheManagerPeerProviderFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
    properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446"/>

<cacheManagerPeerListenerFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>

另外,需要在每个cache属性中加入
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
例如:
<cache name="demoCache"
  maxElementsInMemory="10000"
  eternal="true"
  overflowToDisk="true">
  <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>

总结
    EHCache是一个非常优秀的基于Java的Cache实现。它简单、易用,而且功能齐全,并且非常容易与Spring、 Hibernate等流行的开源框架进行整合。通过使用EHCache可以减少网站项目中数据库服务器的访问压力,提高网站的访问速度,改善用户的体验。
分享到:
评论

相关推荐

    ehcache和memcache性能比较

    在IT行业中,缓存技术是提升应用程序性能的关键手段之一。Ehcache和Memcache是两种广泛使用的缓存解决方案,它们各有特点,适用于不同的场景。本文将深入探讨这两种缓存技术的性能比较,以帮助开发者更好地选择适合...

    连接池方式测试ehcache 独立server的性能

    在IT行业中,优化数据库操作和缓存管理是提高...通过分析和优化这些指标,可以提升整个系统的性能,实现更高效的数据访问。而开源社区的代码分享则提供了学习和实践的宝贵资源,有助于开发者深入理解和应用Ehcache。

    Ehcache 简单的监控

    Ehcache是一个开源的、高性能的缓存解决方案,广泛应用于Java应用程序中,以提高数据访问的速度和效率。本文将深入探讨Ehcache的简单监控,帮助开发者更好地理解其工作原理和性能状态。 首先,了解Ehcache的核心...

    ehcache

    Ehcache 是一个开源的、高性能的缓存解决方案,广泛应用于Java应用程序中,尤其在提升系统性能和减少数据库负载方面表现突出。它支持内存和磁盘存储,并且可以与Java持久层框架如Hibernate、JPA等无缝集成。 ## 1. ...

    ehcache.xsd_ehcache.xml代码提示.rar

    Ehcache是一个广泛使用的开源Java缓存解决方案,它能够提高应用程序性能,通过缓存数据和对象来减少数据库查询。 【描述解析】:描述中提到“已测试有效的ehcache.xsd文件”,这指的是Ehcache的XML Schema定义文件...

    ehcache.jar及源码

    Ehcache是一个广泛使用的开源...通过`ehcache-core-2.5.2.jar`和`ehcache-core-2.5.2-sources.jar`这两个文件,开发者不仅可以使用Ehcache提供的高效缓存服务,还能深入了解其内部机制,进行定制和优化,提升应用性能。

    hibernate+ehcache

    综上所述,Hibernate 与 Ehcache 的整合为 Java 应用提供了高效的数据缓存能力,减少了数据库交互,提升了应用性能。理解和掌握它们的整合方式及优化策略,对于构建高性能的 Java Web 应用至关重要。

    Mybatis-ehcache 1.2.1源码(ehcache-cache-mybatis-ehcache-1.2.1.zip)

    这不仅有助于我们定制自己的缓存解决方案,还能帮助我们在实际项目中更高效地使用Mybatis和Ehcache,提升系统的整体性能。同时,对源码的分析也有助于开发者掌握更高级的Java编程技巧和设计模式,对软件架构有更深入...

    Java缓存框架 EhCache

    作为进程内缓存解决方案,EhCache能够在应用程序运行时存储数据,显著提高数据访问速度,减轻数据库负载,从而提升整体系统性能。 EhCache的核心功能包括: 1. **内存管理**:EhCache将数据存储在内存中,提供了对...

    Ehcache分布式缓存与其在SpringBoot应用

    Ehcache的分布式特性允许在大型系统中进行负载均衡,提高应用性能,同时减少了数据库的压力。 在SpringBoot应用中集成Ehcache,通常需要以下步骤: 1. **环境配置**:首先,你需要在SpringBoot的`pom.xml`文件中...

    mybatis ehcache 1.0 ehcache.xsd 提示文件

    首先,Ehcache是一个开源的、高性能的Java缓存库,它能够极大地减少对数据库的访问,提高应用程序的响应速度。MyBatis通过集成Ehcache,实现了数据的二级缓存,即在SqlSession级别的一级缓存之上,增加了全局的二级...

    ehcache项目缓存技术

    Ehcache是Java平台上的一个开源、高性能的分布式缓存系统,它在处理大量用户访问主页或频繁操作某些数据时,能显著提升系统的响应速度和效率。本篇文章将深入探讨Ehcache项目的缓存技术及其应用。 一、Ehcache简介...

    ehcache资料(包含ehcache jar包)

    **Ehcache** 是一个广泛使用的Java缓存库,它为应用程序提供了本地内存缓存功能,以提高数据访问速度和减少数据库负载。...通过理解并熟练运用Ehcache,开发者可以显著提升应用程序的性能和响应速度。

    ehcache2.6.5.rar

    Ehcache是一个开源的Java缓存库,广泛用于提高应用程序的性能和响应速度,通过存储经常访问的数据在内存中,避免了频繁的数据库查询。它最初由Tomi Triebel开发,现在是Terracotta公司的产品。在版本2.6.5中,...

    ehcache开发工具包

    EhCache是一款高效、易用且功能强大的Java缓存库,它被广泛应用于各种Java应用程序中,以提高数据访问速度并降低数据库负载...无论是简单的小型项目还是复杂的大型系统,EhCache都能提供有效的缓存支持,提升整体性能。

    Ehcache_Hello

    Ehcache是一款开源的Java缓存框架,它被广泛应用于提高应用程序性能,通过存储数据副本以减少对数据库的访问。Ehcache最初由Tomi Tirro设计,现在是Terracotta公司的产品,是Java世界中常用的二级缓存解决方案,尤其...

    Ehcache 3(ehcache-clustered-3.8.1-kit.zip)

    总的来说,Ehcache 3 的集群支持为大型分布式系统提供了高效、可靠的缓存解决方案,通过智能的数据分发和同步机制,确保了数据的一致性和高可用性,同时降低了数据库的访问压力,提升了系统整体的性能。

    Ehcache(一): Spring + Ehcache开场白

    Ehcache是一款高性能、易用且广泛应用于Java环境中的分布式缓存系统,它极大地提高了应用程序的性能和响应速度。在Spring框架中集成Ehcache,能够实现数据的快速访问,减轻数据库的压力,优化整体系统架构。本文将...

    ehcache和h2 database资料

    Ehcache是一款广泛使用的Java缓存库,它允许开发人员在应用程序中高效地管理和存储数据,以提高性能和响应速度。Ehcache的核心特性包括内存和磁盘存储、分布式缓存支持、多级缓存结构以及与Java Transaction API...

Global site tag (gtag.js) - Google Analytics