`
cjnetwork
  • 浏览: 177258 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

java基于filter的应用缓存框架

阅读更多
java web 基于filter的缓存框架







目标、解决的问题:
浏览器客户端向服务器发起许多参数相同的请求,在服务器端的处理之后,在相同参数的情况下,返回的结果一致的情况下,使用该缓存框架,可以提高web服务器的性能。
在java的web开发的时候,有许多请求,在后台处理之后,可能返回相同的数据到浏览器端,或者在一定时间内,返回的结果可能都是一样的。因此可以将执行后的返回结果缓存起来,再下一次相同请求,相同参数的情况向可以直接从缓存中获取到处理结果。例如在一个用户管理系统中,普通的操作有添加、修改、删除、查询4个操作,在执行查询操作的时候,实际上,只要数据库中数据没有发生变化,则返回到浏览器端的数据都是一样的,因此可以给用户的查询增加缓存。
第一步:如下代码:
public String query() {
		String result = ERROR;
		try{
			List<User> allUser = userService.query();
			ServletActionContext.getRequest().setAttribute("allUser", allUser);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

代码中,使用userService从数据库中查询到用户列表,则该用户列表,当数据库中数据没有变化的时候,浏览器端访问该地址都将获取到相同的结果,因此可以将List<User> allUser缓存起来,等待下一次执行该请求的时候,直接从缓存中获取到用户列表,然后跳转到jsp页面做显示。但这种方式,对于单个的方法,需要在业务逻辑增添加代码,而且若这样的需要缓存的地址较多,则是一个比较繁琐的过程,维护多个地方的东西,也不容易。这种方法不是最理想的。考虑到web的请求都是request/response结构的,即都是浏览器端发起请求,由java的web后台处理,完成之后返回浏览器端一个response对象,因此可以直接将response中所有的内容进行缓存,下一次请求的时候直接将上次一次的处理结果中response的内容,设置到新的一次请求返回的response中,这一步可以在更高一层统一完成(filter层)。
查找HttpServletResponse的api后,发现没有直接从response中获取到返回内容的方法,因此采用代理的方式,自己写一个HttpServletResponseProxy,该类代理了所有HttpServletResponse的普通方法,并截获对应的getWriter()、getServletOutputStream()方法,到这一步骤之后,仍旧不能获取到在整个filter业务处理的过程中,往response中写入的内容,因此还需要增加对获取到的Writer的代理、ServletOutputStream的代理,新增加的两个代理PrintWriterProxy、ServletOutputStreamProxy,中便可以获取到写入response的内容,在代理写入方法的时候,写入的同时,将写入的内容copy一份,以便后续使用。那么在第一次请求的时候,所有写入到response的内容就可以获取到,则在第二次请求的时候,可以讲上一次的response中的内容取出并返回给浏览器端。
增加FilterCache.java,可以通过uri地址,判断该地址是否已经请求过,并且缓存尚未过期,没有过期则将缓存内容取出并返回。
第二步:完成到如上步骤,只是完成了简单的缓存,
public String query() {
		String result = ERROR;
		try{
			String username = ServletActionContext.getRequest().getParameter("username");
			List<User> allUser = userService.query(username);
			ServletActionContext.getRequest().setAttribute("allUser", allUser);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

在该段代码中,需要从request中获取到用户名作为查询条件,那么就需要把request中参数考虑到缓存存储的键的生成策略中,而不仅仅只使用请求的地址uri作为键的生成策略,考虑到request中请求的参数很多,为了不时缓存的键过长,需要将request中所有的参数拼接成一个字符串,并同uri地址连接,形成如同"userAction!query.action?username=a"这样的字符串,然后将该字符串进行md5编码,那么就可以得到一个定长且不是太长的字符串作为缓存的键。在拼接字符串的过程中,"userAction!query.action?username=a&groupname=b"和"userAction!query.action?groupname=b&username=a"其实是一样的,因此对从request中获取参数并拼接字符串的过程中,完成之后还需要对参数进行排序,这个可以使用key的字符串大小(英文排序)来确定,将以上可以遇到的两种情况排序后成为一种,然后才可以避免相同地址请求、相同参数的情况下导致缓键生成不同,而实际是相同的情况下,缓存了两个不同的结果的情况。
同样,session中的参数也需要拼接到请求参数中,并影响缓存键的生成。例如:
public String query() {
		String result = ERROR;
		try{
			String username = ServletActionContext.getRequest().getSession().getAttribute("username").toString();
			List<User> allUser = userService.query(username);
			ServletActionContext.getRequest().setAttribute("allUser", allUser);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

第三步:request、session中的参数,有的时候会影响业务逻辑执行的结果,有的时候不会,因此可以将是否影响作为配置项,在缓存策略配置文件中进行配置,那么,各个不同的请求地址的缓存键生成策略部同,可以增加缓存框架的易用性。
第四步:考虑到如果当一个请求正在执行,还没有执行完成,另一个相同请求地址、参数、session参数的请求也请求到服务器端,那么这种情况,将会导致缓存没有起到作用,两次请求都会被web系统执行,消耗了系统资源。因此增加一个同步机制,相同请求地址、参数、session参数即缓存键相同时,后到达的请求,将以线程同步的方式,等待上次执行结果完成,而并不直接调用业务逻辑,等待从缓存中获取到结果。如此,可以在系统缓存到期的情况下,有效防止由于缓存过期引起的性能下降问题。
第五步:在最先提到用户的增加、修改、删除,都会影响到查询的结果。那么还应该设置缓存击穿策略,即当用户增加的时候,击穿查询的缓存结果,当执行增加用户后,上一次的查询结果就失效了,当再次有查询请求道web服务器的时候,将不再从缓存中获取结果,而是再次执行业务逻辑,完成之后将结果缓存。同样用户修改、删除之后也会击穿用户查询的缓存。





使用方式:
1、将jar包和依赖jar包copy到classpath路径中
2、在web.xml中增加缓存过滤器
<!-- 缓存过滤器  -->
	<filter>
		<filter-name>cacheFilter</filter-name>
		<filter-class>
			com.cjnetwork.cache.filter.CacheFilter
		</filter-class>
		<init-param>
			<param-name>configFileLocation</param-name>		<!-- 缓存策略配置文件地址,默认为cache.xml  -->
			<param-value></param-value>
		</init-param>
		<init-param>
			<param-name>requestUriIncludePattern</param-name>	<!-- 缓存过滤器,需要处理的请求的地址的正则表达式  -->
			<param-value>.*?!.*?</param-value>
		</init-param>
		<init-param>
			<param-name>requestUriExcludePattern</param-name>	<!-- 缓存过滤器,需要排除的请求的地址的正则表达式  -->
			<param-value></param-value>
		</init-param>
		<init-param>
			<param-name>xmlReloadable</param-name>	<!-- 策略文件是否为重新导入模式,即当配置文件变化的时候,不需要重新启动应用程序,缓存框架会自动检测到,该功能在开发调制阶段非常有用(推荐在生产环境设置为false)  -->
			<param-value>true</param-value>  
		</init-param>
		<init-param>
			<param-name>cacheProvider</param-name>		<!-- 缓存提供者,可以自定义缓存数据存放方式(内存、其他KeyValue缓存框架等)  -->
			<param-value>com.cjnetwork.cache.provider.SimpleMemeryCacheProvider</param-value>
		</init-param>
	</filter>

3、将filter过滤配置到真正执行业务逻辑的filter之前,如struts框架中,将缓存filter配置到struts的filter之前
<filter-mapping>
		<filter-name>cacheFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

4、修改缓存策略文件cache.xml,该文件可以有filter定义中的初始化参数修改,可以是其他文件名
里面的具体配置参数,请直接查看配置文件中的注释或查看缓存策略配置说明章节。







缓存策略配置说明
通过修改该配置文件,可以调整缓存框架的性能,默认文件为cache.xml文件。
1、修改注解监控的包
<annotationPackage>		<!-- 注解所在的包 -->
		<package uriPrefix="" uriSeperator="!" uriSuffix=".action">com.cjnetwork.gis.action</package>
	</annotationPackage>

annotationPackage节点定义系统中需要扫描的包,在这里设置之后,可以通过注解@Cache完成缓存的配置。节点内的package节点可以定义多个,监听多个包中的注解。
package节点中:
uriPrefix:uri的前缀
uriSeperator:类与方法的分隔符
uriSuffix:uri的后缀

2、配置默认的缓存策略
<defaultCache cacheable="true" cacheTime="180">	<!-- cacheable:默认是否缓存true/false 	cacheTime:默认缓存时间(秒)-->
		<keyFromRequestIncludePattern></keyFromRequestIncludePattern>	<!-- 可存在多个,request的请求参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含 -->
		<keyFromRequestExcludePattern></keyFromRequestExcludePattern>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(包含) -->
		<keyValuePairFromRequestExclude>page=1</keyValuePairFromRequestExclude>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
		<keyValuePairFromRequestExclude>pageSize=10</keyValuePairFromRequestExclude>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
		<keyFromSessionIncludePattern></keyFromSessionIncludePattern>	<!-- 可存在多个,session的参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含 -->
		<keyFromSessionExcludePattern></keyFromSessionExcludePattern>	<!-- 可存在多个,session的参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(包含) -->
		<keyValuePairFromSessionExclude></keyValuePairFromSessionExclude>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
	</defaultCache>


3、修改特殊请求uri的缓存策略
<cache uri="testAction!test.action" cacheable="true" cacheTime="180">	<!-- cacheable:是否缓存true/false 	cacheTime:缓存时间(秒)-->
		<keyFromRequestIncludePattern></keyFromRequestIncludePattern>	<!-- 可存在多个,request的请求参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含 -->
		<keyFromRequestExcludePattern></keyFromRequestExcludePattern>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(不包含) -->
		<keyValuePairFromRequestExclude>page=1</keyValuePairFromRequestExclude>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
		<keyValuePairFromRequestExclude>pageSize=10</keyValuePairFromRequestExclude>	<!-- 可存在多个,request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
		<keyFromSessionIncludePattern></keyFromSessionIncludePattern>	<!-- 可存在多个,session的参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含 -->
		<keyFromSessionExcludePattern></keyFromSessionExcludePattern>	<!-- 可存在多个,session的参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(不包含) -->
		<keyValuePairFromSessionExclude></keyValuePairFromSessionExclude>	<!-- 可存在多个,session的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含 -->
		<effects>
			<effect uri="testAction!test1.action" />	<!-- 可存在多个,缓存击穿地址,表示当目前uri地址testAction!test.action如果真正执行,将击穿uri="testAction!test1.action在系统中的缓存 -->
			<effectBy uri="testAction!test2.action" />	<!-- 可存在多个,缓存击穿地址,表示当testAction!tes2t.action如果真正执行,将击穿目前uri地址testAction!test.action在系统中的缓存 -->
		<effects>
	</cache>


4、注解配置方式(可选)
如果需要使用注解配置,则需要完成配置1中的配置,设置好相关的属性
@com.cjnetwork.cache.annotation.Cache具有如下属性
uri:请求的地址
cacheable:是否缓存true/false
cacheTime:缓存时间
keyFromRequestIncludePattern:request的请求参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含
keyFromRequestExcludePattern:request的请求参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(不包含)
keyValuePairFromRequestExclude:request的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含
keyFromSessionIncludePattern:session的参数中,需要包含在缓存主键生成中,不配置表示请求中所有参数都要包含
keyFromSessionExcludePattern:session的参数中,需要从缓存主键生成中排除,不配置表示请求中所有参数都不排除(不包含)
keyValuePairFromSessionExclude:可存在多个,session的请求参数中,需要从缓存主键生成中排除的特殊键值对,例如在请求某个查询的时候,有page=1的参数和不带该参数的查询,其结果应该是一样的,不配置表示请求中所有参数都要包含
effect:缓存击穿地址
effectBy:被其他uri地址击穿,其他地址将影响该地址的其他的地址









修改记录:
2012-08-10 cjnetwork
FilterCache-V1.0.1-realease.jar
1、修正相同请求地址,不同参数之间数据不同,但获取到相同结果bug
2、修正缓存测量文件中,无注解配置项是初始化异常

2012-05-17 cjnetwork
FilterCache-V1.0.0-realease.jar
1、完成第一个版本,通过配置能够完成缓存功能,正确配置缓存参数,可以提高web可访问性数倍;
2、该版本没有缓存击穿功能,下一版本将实现该功能;
  • 大小: 45.9 KB
分享到:
评论
1 楼 coldrain2014 2014-11-18  
更多html、js、php、java教程,请关注http://www.code1314.com/

相关推荐

    Java的学习之路,学习JavaEE以及框架时候的一些项目,结合博客和源码,让你受益匪浅,适合Java初学者和刚入门开始学框架者

    [Spring]基于Spring框架的Web应用演示(附带cglib工具进行动态代理) [Tomcat7.0]Tomcat7版本安装包 [UltraISO]制作U盘启动盘需要的 [log4j_jar]log4j的支持包 [myAutoLoginWeb]过滤器Filter学习-实现用户的自动...

    JavaEE求职简历-姓名-JAVA开发工程师.docx

    2.熟悉Java Web应用开发,熟悉listener、EL、JSTL、Filter等常用技术; 3.熟悉Spring MVC、Spring Boot、Spring Data、Spring Security的使用,了解Spring的Ioc与Aop思想; 4.拥有基于Spring整合常用框架并进行项目...

    mysql spring c3p0/dbcp/dbUtils工具支持包

    [Spring]基于Spring框架的Web应用演示(附带cglib工具进行动态代理) [Tomcat7.0]Tomcat7版本安装包 [UltraISO]制作U盘启动盘需要的 [log4j_jar]log4j的支持包 [myAutoLoginWeb]过滤器Filter学习-实现用户的自动登录与...

    新版Android开发教程.rar

    � 由于采用了 Java 作为应用开发语言,目前可用的传统第三方应用还很少,但由于 Android 是一款完全 开 源的移动计算平台,相信第三方应用会很快的丰富起来。 � Google 提供了一套 Java 核心包 (J2SE 5,J2SE 6) 的...

    spring4.3.9相关jar包

    spring-web.jar(必须) :这个jar 文件包含Web 应用开发时,用到Spring 框架时所需的核心类,包括自动载入Web Application Context 特性的类、Struts 与JSF 集成类、文件上传的支持类、Filter 类和大量工具辅助类。...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part2

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part3

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part1

    而Spring Boot 是Spring 主推的基于“习惯优于配置”的原则,让你能够快速搭建应用的框架,从而使得Java EE 开发变得异常简单。 《JavaEE开发的颠覆者: Spring Boot实战》从Spring 基础、Spring MVC 基础讲起,从而...

    最新最全的spring开发包

     这个jar文件包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。 (12) spring-webmvc.jar 这个...

    spring4.1核心包

    包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。 18. spring-webmvc-4.1.1.RELEASE.jar 包含...

    asp.net知识库

    Tool Tip 示例(FILTER版) Tool Tip示例 (htc版) 一个.net发送HTTP数据实体的类 按键跳转以及按Enter以不同参数提交,及其他感应事件 动态控制Page页的Head信息 SubmitOncePage:解决刷新页面造成的数据重复提交...

    spring jar 包详解

    (11) spring-web.jar 这个jar文件包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、 Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。 (12) ...

    jfinalpluginsjfinal-dreampie.zip

    //根据key值来移除基于某个特定方法的缓存,而不是移除整个controller下的缓存 String controllerKey = ai.getControllerKey();  if (!ValidateUtils.me().isNullOrEmpty(removeCacheName)) {  String ...

    ssh(structs,spring,hibernate)框架中的上传下载

    SSH各框架的均为当前最新版本:  •Struts 1.2  •Spring 1.2.5  •Hibernate 3.0  本文选用的数据库为Oracle 9i,当然你可以在不改动代码的情况下,通过配置文件的调整将其移植到任何具有Blob字段类型的数据库...

    spring security 参考手册中文版

    13.5与其他基于过滤器的框架一起使用 118 13.6高级命名空间配置 118 14.核心安全筛选器 119 14.1 FilterSecurityInterceptor 119 14.2 ExceptionTranslationFilter 121 14.2.1 AuthenticationEntryPoint 122 14.2.2 ...

    Spring 2.5 jar 所有开发包及完整文档及项目开发实例

     这个jar文件包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。 (12) spring-webmvc.jar  这个...

    Hadoop实战中文版.PDF

    出版信息编辑译者:韩冀中出版社:人民邮电出版社出版时间:2011年10月版次:1.1开本:16开装帧:平装字数:417千字页数:253页内容简介编辑作为云计算所青睐的分布式架构,Hadoop是一个用Java语言实现的软件框架,...

    Sosoo 1.0网络爬虫程序.doc

    通过上述对功能的定制,我们可以看到在应用中我们对sosoo的编程接口并不多,而且目前系统都是基于set的方式注入aop注入对象,这样很容易和spring等基于set方式的依赖注入(IOC)框架集成。 1.Roboter类,spider...

Global site tag (gtag.js) - Google Analytics