在界面访问过程中,存在相同参数多个用户多次同时间段访问时,每次直接由业务逻辑生成返回数据,造成数据库等组件访问瓶颈。
解决思路,是通过缓存组件,数据库或者文件系统对相同的参数访问,且无需精确的数据结果进行设定更新周期的缓冲。
具体方法通过java注解,或者宏(c) 设置接口是否需要进行缓冲处理。然后在切面(宏)中加入相应的逻辑处理。下面以java为例子举例说明:
@Documented
@Retention(RUNTIME)
@Target({ ElementType.METHOD })
public @interface Staticable {
//缓存时间 默认30分钟
int timelapse() default 30;
//数据无效时,是否同步更新,还是延后更新 1:同步更新
int freshSynchro() default 2;
}
切面代码:
package com.config.aspect;
import java.lang.reflect.Method;
import java.util.HashMap;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSONObject;
import com.common.CommonResult;
import com.common.ResultStatus;
import com.config.annotation.Staticable;
import com.config.aspect.thread.StaticThread;
import com.util.HttpCacheUtil;
/**
* @Description: 页面静态化
* @author liyw
* @date 2020年10月29日 下午7:18:48
*
*/
@Aspect
@Component
public class StaticableAspect{
private static final Logger log = Logger.getLogger(StaticableAspect.class);
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* @Description: 注解@Staticable拦截
*/
@Pointcut("@annotation(com.config.annotation.Staticable) "
+ "|| @within(com.config.annotation.Staticable)")
protected void Staticable() {}
@SuppressWarnings("unchecked")
@Around("Staticable()")
@Order(10)
public Object staticAround(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// 从request信息中获取参数
String url = request.getServletPath().toString();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method1 = signature.getMethod();
Integer timeLapse = 30;
Integer freshSynchro = 2;
//获取方法参数注解
Staticable staticable = method1.getAnnotation(Staticable.class);
timeLapse = staticable.timelapse();
buffByPage = staticable.buffByPage();
freshSynchro = staticable.freshSynchro();
if (freshSynchro == null || freshSynchro != 2) {
freshSynchro = 1;
}
if (timeLapse == null) {
timeLapse = 30;
}
Map<String, String[]> parameterMap = request.getParameterMap();
JSONObject var = (JSONObject)JSONObject.toJSON(parameterMap);
//以访问串和参数json串的md5值作为缓存的key值
String keys = HttpCacheUtil.getKey(url, var.toJSONString());
//取数据异常时,或符合取逻辑时执行实时取数据
try {
//取缓存是否超时
Boolean isOverTime = HttpCacheUtil.overTime(keys);
//未超时, 或超时延时更新时, 取缓存数据返回
if(!isOverTime || freshSynchro == 2){
Object rett = HttpCacheUtil.getCache(keys);
if(log.isDebugEnabled()) {
log.debug("cache success : ->" + url);
}
if (isOverTime) {
//超时且延期更新时,启动后续线程执行更新执行
if(log.isDebugEnabled()) {
log.debug("refresh success : ->" + url);
}
//线程后台完成更新数据
threadPoolTaskExecutor.execute(new StaticThread(pjp, keys, timeLapse));
}
return rett;
}
}catch(Exception e) {
}
Object ret = pjp.proceed();
CommonResult<?> a = (CommonResult<?>) ret;
if ( a != null && ResultStatus.SUCCESS.getStatus() == a.getStatus()) {
HttpCacheUtil.setCache(keys, a, timeLapse);
}
return ret;
}
}
以上只完成从缓存进行缓存,没有实现数据库与文件缓存。
p
ackage com.config.aspect.thread;
import org.aspectj.lang.ProceedingJoinPoint;
import com.common.CommonResult;
import com.common.ResultStatus;
import com.zjhcsoft.util.HttpCacheUtil;
public class StaticThread implements Runnable{
private ProceedingJoinPoint pjp = null;
private String key = "";
private Integer bufTimes = 30;
public StaticThread(ProceedingJoinPoint pjp, String key, Integer bufTimes) {
this.pjp = pjp;
this.key = key;
this.bufTimes = bufTimes;
}
public void run() {
try {
Object ret = pjp.proceed();
CommonResult<?> a = (CommonResult<?>) ret;
if ( a != null && ResultStatus.SUCCESS.getStatus() == a.getStatus()) {
//执行成功,更新缓存
HttpCacheUtil.setCache(key, a, bufTimes);
}
}catch(Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
package com.util;
import com.common.CommonResult;
import com.exception.MngException;
import com.cache.CacheManagerFactory;
import com.cache.ICache;
import com.util.Md5Util;
/**
* @Author: fanjiajie
* @Date: 2020/12/7 11:08
*/
public class HttpCacheUtil {
private static ICache cache = null;
static {
try {
//这里的CacheManagerFactory 可以用Redis来代替
cache = CacheManagerFactory.getCache();
} catch (Exception e) {
}
}
public static CommonResult<?> getCache(String key) throws Exception{
CommonResult<?> ret = (CommonResult<?>)cache.get(key);
if (ret == null) {
throw new MngException("暂无缓存操作");
}
return ret;
}
public static void setCache(String key, CommonResult<?> value, Integer bufTimes)throws Exception{
Integer iMinutes = bufTimes;
cache.put(key, value);
Long timeOverTime = System.currentTimeMillis() + iMinutes * 60 * 1000;//分钟
cache.put(key + "_time", timeOverTime);
}
public static String getKey(String function, String params) {
String key ="FN" + function + "_" + new Md5Util().getMD5ofStr(params);
return key;
}
public static Boolean overTime(String key) {
try {
Long timeOverTime = (Long)cache.get(key + "_time");
if (System.currentTimeMillis() > timeOverTime) {
return true;
}else {
return false;
}
}catch(Exception e){
}
return true;
}
}