spring+ehcache实战--性能优化之道
在做系统集成平台项目的时候遇到了一个比較麻烦的问题。原因是使用考试系统的时候所依赖的是基础系统公布的webservice来获取基础数据,webservice的跨网络传输本身或多或少会对系统性能产生一定影响再加上传输的数据量比較大这样对系统性能的影响就更大了,可是导致系统性能下降的另外一个原因就是频繁的打开关闭数据库。针对这两个问题我们採取了两个解决方式以期将性能影响降至最低第一就是webservice由原先的传输序列化对象改为传输json串,第二个就是针对数据库连接的开闭问题作了缓存处理。本文我们主要探讨第二个解决方式ehcache。
ehcache是一个很不错的缓存框架,配置前来简单而且功能强大,在项目中加缓存的地方主要有两处,第一是缓存实体对象。这层缓存加在实体层,主要使用的是hibernate的二级缓存(同一时候一定要开启查询缓存)利用spring的AOP注解就可以简单搞定,而在其它查询方法上主要用的就是ehcache,用来缓存方法返回的各种对象。开启hibernate的查询缓存和二级缓存比較简单。在此不做过多介绍,我们主要来看ehcache的使用方法。
1.首先我们用到的是Interceptor,定义两个拦截器MethodCacheInterceptor和MethodCacheAfterAdvice,前者主要用来拦截以get和find开头的方法(用于缓存结果)。而第二个拦截器主要用来拦截以update开头的方法,用来清除缓存。以下让我们来看一下详细的代码:
public class MethodCacheInterceptor implements MethodInterceptor,
InitializingBean {
private static final Log logger = LogFactory
.getLog(MethodCacheInterceptor.class); private Cache cache; public void setCache(Cache cache) {
this.cache = cache;
} public MethodCacheInterceptor() {
super();
} /**
* 拦截Service/DAO 的方法,并查找该结果是否存在,假设存在就返回cache 中的值, 31 *
* 否则,返回数据库查询结果。并将查询结果放入cache 32
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result; logger.debug("Find object from cache is " + cache.getName()); String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey); if (element == null) {
logger
.debug("Hold up method , Get method result and create cache........!");
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
System.out.println("-----非缓存中查找。查找后放入缓存");
cache.put(element);
}else{
System.out.println("----缓存中查找----");
}
return element.getValue();
} /**
* 获得cache key 的方法,cache key 是Cache 中一个Element 的唯一标识 55 * cache key
* 包含包名+类名+方法名,如 com.co.cache.service.UserServiceImpl.getAllUser 56
*/
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();
} /**
* implement InitializingBean。检查cache 是否为空 70
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
} }
第二个拦截器的代码例如以下:
public class MethodCacheAfterAdvice implements AfterReturningAdvice,
InitializingBean {
private static final Log logger = LogFactory
.getLog(MethodCacheAfterAdvice.class); private Cache cache; public void setCache(Cache cache) {
this.cache = cache;
} public MethodCacheAfterAdvice() {
super();
} public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for (int i = 0; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if (cacheKey.startsWith(className)) {
cache.remove(cacheKey);
System.out.println("------清除缓存----");
logger.debug("remove cache " + cacheKey);
}
}
} public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
} }
有了这两个拦截器,接下来我们所要做的就是为将这两个拦截器引入进项目让其发挥作用,这些配置都在ehcache.xml文件里进行,以下来看该文件的详细配置:
<?xml version="1.0" encoding="UTF-8"? >
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 引用ehCache 的配置-->
<bean id="defaultCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>ehcache.xml</value>
</property>
</bean> <!-- 定义ehCache 的工厂,并设置所使用的Cache name -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="defaultCacheManager" />
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean> <!-- find/create cache 拦截器-->
<bean id="methodCacheInterceptor" class="com.co.cache.ehcache.MethodCacheInterceptor">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!-- flush cache 拦截器-->
<bean id="methodCacheAfterAdvice" class="com.co.cache.ehcache.MethodCacheAfterAdvice">
<property name="cache">
<ref local="ehCache" />
</property>
</bean> <bean id="methodCachePointCut"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor" />
</property>
<property name="patterns">
<list>
<value>.*find.*</value>
<value>.*get.*</value>
</list>
</property>
</bean>
<bean id="methodCachePointCutAdvice"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheAfterAdvice" />
</property>
<property name="patterns">
<list>
<value>.*create.*</value>
<value>.*update.*</value>
<value>.*delete.*</value>
</list>
</property>
</bean>
</beans>
这样就将拦截器的配置以及缓存配置引入导了项目中,缓存配置信息主要放在ehcache.xml文件里,具体信息例如以下:
<ehcache>
<diskStore path="H:\\temp\\cache" />
<defaultCache maxElementsInMemory="1000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" />
<cache name="DEFAULT_CACHE" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="300000" timeToLiveSeconds="600000" overflowToDisk="true" />
</ehcache>
至此我们的对应配置都已经做好了,以下让我们建立測试类来測试缓存是否起作用,在这里我们主要用的类有三个,来看详细代码:
public interface TestService {
public List getAllObject(); public void updateObject(Object Object);
}
TestService是调用接口,而以下的TestServiceImpl是事实上现,代码例如以下:
public class TestServiceImpl implements TestService {
public List getAllObject() {
System.out.println("---TestService:Cache 内不存在该element。查找并放入Cache。");
return null;
} public void updateObject(Object Object) {
System.out.println("---TestService:更新了对象,这个Class 产生的cache 都将被remove!");
}
}
以下的JunitTestClass为真正的測试类。代码例如以下:
public class JunitTestClass { @Test
public void testRun(){
String DEFAULT_CONTEXT_FILE = "/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(
DEFAULT_CONTEXT_FILE);
TestService testService = (TestService) context.getBean("testService"); //第一次查找
testService.getAllObject(); //第二次查找
testService.getAllObject(); //运行update方法(应该清除缓存)
testService.updateObject(null); //第三次查找
testService.getAllObject();
}
}
分析測试代码,当第一次运行getAllObject()方法的时候因为是第一次运行查询操作,会被MethodCacheInterceptor拦截。当MethodCacheInterceptor发现没有命中缓存的时候,运行invoke()方法。让程序去数据库查询(本程序中仅仅是模拟了对数据库的查询,并没有真正查询数据库。只是其所表达的意思是与查询数据库没有差别的),我们看到这是会运行TestServiceImpl的getAllObject()方法,打印出一条语句同一时候打印拦截器中的“-----非缓存中查找。查找后放入缓存”语句。当第二次运行该方法的时候因为已经存在了缓存,所以不再运行TestServiceImpl的getAllObject()方法。同一时候仅仅打印拦截器中的“----缓存中查找----”语句,当运行updateObject()方法的时候会被MethodCacheAfterAdvice拦截,并运行TestServiceImpl的updateObject()方法,所以会打印“---TestService:更新了对象。这个Class
产生的cache 都将被remove”语句以及拦截器中的“------删除缓存----”语句,当运行第三次查找的时候。因为缓存已经被清除,所以会在此输出和第一次一样的语句,以下来验证一下我们的猜測是否正确:
输出结果与我们推測的一样,也就是说此时ehcache缓存在程序中已经起作用了。
spring+ehcache实战--性能优化之道的更多相关文章
- for循环实战性能优化之使用Map集合优化
笔者在<for循环实战性能优化>中提出了五种提升for循环性能的优化策略,这次我们在其中嵌套循环优化小循环驱动大循环的基础上,借助Map集合高效的查询性能来优化嵌套for循环 ...
- 使用Spring Ehcache二级缓存优化查询性能
最近在对系统进行优化的时候,发现有些查询查询效率比较慢,耗时比较长, 通过压测发现,主要耗费的性能 消耗在 查询数据库,查询redis 数据库:连接池有限,且单个查询不能消耗大量的连接池,占用大量IO ...
- MongoDB实战性能优化
1. 性能优化分类 mongodb性能优化分为软件层面和操作系统层面. 软件层面,一般通过修改mongodb软件配置参数来达到,这个需要非常熟悉mongodb里面的各种配置参数: 而操作系统层面,相对 ...
- MySQL性能优化之道
1.in和not in子查询优化 not in 是不能命中索引的,所以以下子查询性能很低. 如果是确定且有限的集合时,可以使用.如 IN (0,1,2). 用 exists或 notexists代替 ...
- 【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战
一.写在前面 相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这是非常火的一门技术. 如果只是用户量很少的传统IT系统,使用Spring Cloud可能还暴露不出 ...
- Spring/Hibernate 应用性能优化的7种方法
对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能.此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法.本文系 O ...
- for循环实战性能优化
完成同样的功能,用不同的代码来实现,性能上可能会有比较大的差别,所以对于一些性能敏感的模块来说,对代码进行一定的优化还是很有必要的.今天就来说一下java代码优化的事情,今天主要聊一下对于for(wh ...
- Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势
原创文章,同步首发自作者个人博客转载请务必在文章开头处注明出处. 摘要 本文结合实例详细阐明了Spark数据倾斜的几种场景以及对应的解决方案,包括避免数据源倾斜,调整并行度,使用自定义Partitio ...
- Spring Cloud Feign 性能优化
#### 1.替换 tomcat 首先,把 tomcat 换成 undertow,这个性能在 Jmeter 的压测下,undertow 比 tomcat 高一倍 **第一步,pom 修改去除tomca ...
随机推荐
- nyoj592 spiral grid
spiral grid 时间限制:2000 ms | 内存限制:65535 KB 难度:4 描述 Xiaod has recently discovered the grid named &q ...
- IDEA开发工具常用快捷键总结
Ctrl+Alt+V 快速补全变量名 Alt+Enter 自动修复 psvm 生成main方法 Ctrl+X 删除一行 Ctrl+D 复制一行 Ctrl+N 查找类 Ctrl+P 显示方法参数 Ctr ...
- vue2的全局变量的设置
最近在学习VUE.js 中间涉及到JS全局变量,与其说是VUE的全局变量,不如说是模块化JS开发的全局变量. 1.全局变量专用模块 就是以一个特定模块来组织管理这些全局量,需要引用的地方导入该模块便好 ...
- uC/OS-III 概要
本章主要对 uC/OS-III 实时操作系统做一些概要介绍,使读者对 uC/OS-III 有个整体的浅 认识,为后面的章节的详细讲解做一个铺垫. 下图是 uC/OS-III 系统从底层到上层的文件结构 ...
- Hibernate- QBC查询方式
QBC查询方式 01.基本查询 02.组合查询 03.关联查询 04.分页查询 05.QBE查询 06.离线查询
- MySql: ”Commands out of sync“Error (Connect/C++)
使用 Connector/C++ 查询 Mysql , 连续调用存储过程时 会出现如下: Commands out of sync; you can't run this command now,st ...
- Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图
接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...
- e646. 处理鼠标点击事件
component.addMouseListener(new MyMouseListener()); public class MyMouseListener extends MouseAdapter ...
- 不要忘记最后那个 default 分支
不要忘记最后那个 default 分支. 即使程序真的不需要 default 处理, 也应该保留语句 default : break; 这样做并非多此一举,而是为了防止别人误以 为你忘了 defaul ...
- Ubuntu系统启动报错:The system is running in low-graphics mode
最近,不小心将自己的Ubuntu-12.04桌面系统搞坏了,主要是由于改变了/var目录下文件的属主,结果桌面系统崩溃了,启动都成问题了.不过还算幸运,可以通过其他的机器登录到我的系统上.根据别人的系 ...