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缓存在程序中已经起作用了。

时间: 2024-08-20 13:36:34

spring+ehcache实战--性能优化之道的相关文章

【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

一.写在前面   相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这是非常火的一门技术. 如果只是用户量很少的传统IT系统,使用Spring Cloud可能还暴露不出什么问题. 如果是较多用户量,高峰每秒高达上万并发请求的互联网公司的系统,使用Spring Cloud技术就有一些问题需要注意了.     二.场景引入,问题初现 先不空聊原理.理论,来讲一个真实的例子,这是我的一个朋友在创业互联网公司发生过的真实案例. 朋友A的公司做互联网类的创业,组建了一个小型

for循环实战性能优化

完成同样的功能,用不同的代码来实现,性能上可能会有比较大的差别,所以对于一些性能敏感的模块来说,对代码进行一定的优化还是很有必要的.今天就来说一下java代码优化的事情,今天主要聊一下对于for(while等同理)循环的优化,它作为三大结构之一的循环,在我们编写代码的时候会经常用到.循环结构让我们操作数组.集合和其他一些有规律的事物变得更加的方便,但是如果我们在实际开发当中运用不合理,可能会给程序的性能带来很大的影响.所以我们还是需要掌握一些技巧来优化我们的代码的. 1 嵌套循环 private

Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势

原创文章,转载请务必将下面这段话置于文章开头处.本文转发自技术世界,原文链接 http://www.jasongj.com/spark/skew/ 摘要 本文结合实例详细阐明了Spark数据倾斜的几种场景以及对应的解决方案,包括避免数据源倾斜,调整并行度,使用自定义Partitioner,使用Map侧Join代替Reduce侧Join,给倾斜Key加上随机前缀等. 为何要处理数据倾斜(Data Skew) 什么是数据倾斜 对Spark/Hadoop这样的大数据系统来讲,数据量大并不可怕,可怕的是

一文学会JVM性能优化

实战性能优化 1 重新认知JVM 之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程,现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图. 执行引擎:用于执行JVM字节码指令 主要由两种实现方式: (1)将输入的字节码指令在加载时或执行时翻译成另外一种虚拟机指令: (2)将输入的字节码指令在加载时或执行时翻译成宿主主机本地CPU的指令集.这两种方式对应着字节码的解释执行和即时编译. 9.2 堆内存溢出 9.2.1 代码 记得设置参数比如-Xmx20M -Xms2

java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘

15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; color: #FF0 } 15套java架构师.集群.高可用.高可扩展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  clo

15套java互联网架构师、高并发、集群、负载均衡、高可用、数据库设计、缓存、性能优化、大型分布式 项目实战视频教程

* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Redis.ActiveMQ.Nginx.Mycat

java架构师大型分布式综合项目实战,高并发,集群,高可用,程序设计,性能优化,架构设计,负载均衡,大数据量

* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Redis.ActiveMQ.Nginx.Mycat

15套java架构师、集群、高可用、高可扩 展、高性能、高并发、性能优化大型分布 式项目实战视频教程

2017-08-09 * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Redis.ActiveMQ.

蚂蚁金服架构师带你深入性能优化一MySql性能优化实战

概要: Mysql的优化,大体可以分为三部分:索引的优化,sql语句的优化,表的优化.本文主要帮助自己整理思路,也可作为一个学习MySQL优化的提纲. 索引的优化 只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引 尽量使用短索引,如果可以,应该制定一个前缀长度 对于经常在where子句使用的列,最好设置索引,这样会加快查找速度 对于有多个列where或者order by子句的,应该建立复合索引 对于like语句,以%或者'-'开头的不会使用索