您的位置:首页 > 编程语言 > Java开发

javaEE 使用ServletContext实现服务器端简单定时更新缓存

2014-07-14 17:14 405 查看
我在做一个门户系统的时候遇到webService的性能问题,当时由于设计中webService传递的数据是非结构化的,因此需要建立大量的链接获取数据。后期测试时webService访问很慢,大概要7秒钟才能完成一个页面的数据。当时不想再更改webService服务器以及客户端代码了,就想着实现一个缓存,用户访问门户页面的时候,不是直接访问webService来获取数据,而是直接从缓存中查找,然后每5分钟调用webService更新一下门户系统的缓存,这样来优化页面的响应时间。

首先要注册一个ServletContextListener,这个监听器有两个方法(contextInitialized,contextDestroyed)分别是web应用启动和销毁的时候调用的。

在web应用启动的时候,调用webService,获取初始数据,放在ServletConetxt中

[java] view
plaincopy

package com.leec.yetsoon.listener;

import java.util.Date;

import java.util.Map;

import javax.servlet.ServletContext;

import javax.servlet.ServletContextEvent;

import javax.servlet.ServletContextListener;

public class CacheListenter implements ServletContextListener{

public void contextDestroyed(ServletContextEvent event) {

System.out.println("contextDestroyed");

}

public void contextInitialized(ServletContextEvent event) {

//查询数据库获得所要共享的信息,获取需要缓存的信息,以map形式保存

Map<String, Object> cacheMap=CacheMapFactory.getCacheMap();

//获得ServletContext实例

ServletContext context = event.getServletContext();

//将查询到的共享信息保存到ServletContext中 context.setAttribute();

context.setAttribute("cacheMap", cacheMap);

//将更新时间加入,以便实现定时刷新

context.setAttribute("preDate", new Date());

context.setAttribute("isRefreshing", false);

}

}

其次,还要再注册一个ServletRequestListener,监听每次客户端请求,当请求到来的时间比缓存中的上次更新时间超过5分钟的时候,调用webService,更新缓存。

[java] view
plaincopy

package com.leec.yetsoon.listener;

import java.util.Date;

import java.util.Map;

import javax.servlet.ServletContext;

import javax.servlet.ServletRequestEvent;

import javax.servlet.ServletRequestListener;

public class TimeCountListener implements ServletRequestListener {

private final static float CACHE_MAX_AGE = 5 * 60 * 1000;//定时5分钟

public void requestDestroyed(ServletRequestEvent arg0) {

}

public void requestInitialized(ServletRequestEvent event) {

ServletContext context = event.getServletContext();

if(!(Boolean)context.getAttribute("isRefreshing")

&& ((new Date()).getTime() - ((Date)context.getAttribute("preDate")).getTime()) > CACHE_MAX_AGE){

context.setAttribute("isRefreshing", true);

//在这里再次查询数据库,并将ServletContext中的信息更新

Map<String, Object> cacheMap=CacheMapFactory.getCacheMap();

<span style="white-space:pre"> </span>context.setAttribute("cacheMap", cacheMap);

context.setAttribute("preDate", new Date());//每次更新缓存的同时也更新时间

context.setAttribute("isRefreshing", false);

}

}

}

这样就不用每次都消耗大量资源访问webService了~

这个缓存还存在一些问题,就是某个用户请求页面的时候,监听器接收到请求,并且满足

[java] view
plaincopy

!(Boolean)context.getAttribute("isRefreshing")

&& ((new Date()).getTime() - ((Date)context.getAttribute("preDate")).getTime()) > CACHE_MAX_AGE

条件时,进行缓存更新,这个过程是同步的,只有等待更新完毕,页面才能显示出来,这样对某些运气不好的个别客户端来讲,这个页面响应的时间是不可忍受的。

因此可以把更新缓存的动作改成异步的。以下代码没有进行过测试:

[java] view
plaincopy

package com.leec.yetsoon.listener;

import java.util.Date;

import java.util.Map;

import javax.servlet.ServletContext;

import javax.servlet.ServletRequestEvent;

import javax.servlet.ServletRequestListener;

public class TimeCountListener implements ServletRequestListener {

private final static float CACHE_MAX_AGE = 5 * 60 * 1000;//定时5分钟

public void requestDestroyed(ServletRequestEvent arg0) {

}

public void requestInitialized(ServletRequestEvent event) {

final ServletContext context = event.getServletContext();

if(!(Boolean)context.getAttribute("isRefreshing")

&& ((new Date()).getTime() - ((Date)context.getAttribute("preDate")).getTime()) > CACHE_MAX_AGE){

context.setAttribute("isRefreshing", true);

//在这里再次查询数据库,并将ServletContext中的信息更新

Thread t = new Thread(new Runnable(){

public void run(){

Map<String, Object> cacheMap=CacheMapFactory.getCacheMap();

context.setAttribute("cacheMap", cacheMap);

}

});

t.start();

context.setAttribute("preDate", new Date());

context.setAttribute("isRefreshing", false);

}

}

}

另外一个问题,我在context中保存了一个状态--isRefreshing,每次在更新前

[java] view
plaincopy

context.setAttribute("isRefreshing", true);

把状态设为正在更新,更新完毕之后,把状态再修改回去

context.setAttribute("isRefreshing", false);

每次更新的时候是要检查这个状态的,如果是正在更新,就不会再次更新,但是setAttribute的操作不是原子的,因此也可能有多个用户进入到更新缓存的状态,这个进入的会不会经常发生也没有在生产条件下测试过,因此上面的这个缓存并发性很弱,能不能应用到生产环境很难保证~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: