项目启动后启动一个线程不断刷新
package web.m.reporter.task.refresh;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import web.m.reporter.common.cache.ReFresh;
@Component
@Slf4j
@Order(value = 1)
public class RefreshCacheTask implements CommandLineRunner {
@Autowired
private ReFresh fresh;
@Override
public void run(String... args) throws Exception {
while (true) {
try {
fresh.reFresh();
Thread.sleep(10000L);
} catch (Exception e) {
log.error("刷新缓存异常-" + e.getMessage(), e);
}
}
}
}
缓存切面工具
package web.m.reporter.common.cache;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import redis.Gcache;
import web.m.reporter.common.RedisKey;
import web.m.reporter.dao.OrderDAO;
import web.m.reporter.domain.OrderDimension;
import web.m.reporter.domain.TimeDivision;
import web.m.reporter.external.CallMallData;
import web.m.reporter.external.vo.ShopVo;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
/**
-
缓存拦截器
-
-
auth:fengzk
-
date:2022/04/19
*/
@Aspect
@Slf4j
@Component
public class CacheHandlerAspect {@Autowired
private Gcache gcache;
@Autowired
private ReFresh reFresh;private final static String SALT = “fds123”;
@Pointcut(“@annotation(web.m.reporter.common.cache.CacheTarget)”)
public void pointcut() {
}/**
- 基于拦截器来生成指纹
- @param joinPoint 入参
- @return 切面返回
*/
@Around(“pointcut()”)
public Object pointcut(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return getDate(joinPoint);
} catch (Throwable e) {
log.error(“切面缓存异常” + e.getMessage(), e);
}
return null;
}
/**
- 获取缓存数据
- @param joinPoint 切入点
- @return 返回数据
*/
public Object getDate(ProceedingJoinPoint joinPoint) throws Throwable {
return getDateHandler(joinPoint, Boolean.TRUE);
}
public Object getDateHandler(ProceedingJoinPoint joinPoint, Boolean runCache) throws Throwable {
CacheTarget cacheTarget = getcacheTarget(joinPoint);
String key = getCacheKey(joinPoint, cacheTarget);
String cacheValue = gcache.get(key);
if (null != cacheValue && runCache) {
Object ob = cover(joinPoint, cacheValue);
if (null != ob) {
log.info(“缓存中获取-” + ob.toString());
return ob;
}
}
Object proceed = joinPoint.proceed();
save(key, joinPoint, cacheTarget);
if (null != proceed && CacheParams.refresh.equals(cacheTarget.refresh())) {
reFresh.addTask(joinPoint);
}
return proceed;
}private Object cover(ProceedingJoinPoint joinPoint, String cacheValue) {
Class<?> clazz = joinPoint.getTarget().getClass(); String className = clazz.getName(); String methodName = joinPoint.getSignature().getName(); if (className.equals(CallMallData.class.getName())) { if (methodName.equals("getShopCode")) { return JSONArray.parseArray(cacheValue).toJavaList(ShopVo.class); } } Class<?>[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
if (interfaces[0].getName().equals(OrderDAO.class.getName())) {
if (methodName.equals(“getOrderDimension”) ||
methodName.equals(“getOrderDimensionSeveralDays”)) {
return JSONObject.parseObject(cacheValue).toJavaObject(OrderDimension.class);
}
if (methodName.equals(“getOrderDimensionList”)) {
return JSONArray.parseArray(cacheValue).toJavaList(OrderDimension.class);
}
if (methodName.equals(“getTimeDivisionsOfRealAmount”) ||
methodName.equals(“getTimeDivisionsOfPayAmount”) ||
methodName.equals(“getTimeDivisionsOfPayOrderNumber”) ||
methodName.equals(“getTimeDivisionsOfRefundOrderNumber”) ||
methodName.equals(“getTimeDivisionsOfRealAmountSeveralDays”) ||
methodName.equals(“getTimeDivisionsOfPayAmountSeveralDays”) ||
methodName.equals(“getTimeDivisionsOfPayOrderNumberSeveralDays”) ||
methodName.equals(“getTimeDivisionsOfRefundOrderNumberSeveralDays”)) {
return JSONArray.parseArray(cacheValue).toJavaList(TimeDivision.class);
}
}} return null;
}
/**
- 数据缓存
- @param key 缓存key
- @param joinPoint 切面上下文
- @throws Throwable 异常
*/
private void save(String key, ProceedingJoinPoint joinPoint,
CacheTarget cacheTarget) throws Throwable {
Object json = JSONObject.toJSON(joinPoint.proceed());
Integer time = Integer.parseInt(cacheTarget.time());
time = time > Integer.parseInt(CacheParams.time) ?
Integer.parseInt(CacheParams.time) : time;
if (null != json) {
gcache.setex(key, time, String.valueOf(json));
}
}
/**
- 获取缓存key
- @param joinPoint 接口入参
- @return 缓存key
*/
public String getCacheKey(ProceedingJoinPoint joinPoint, CacheTarget cacheTarget) {
StringBuilder params = new StringBuilder();
if (joinPoint.getArgs().length > 0) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
Object o = joinPoint.getArgs()[i];
// 获取切面请求的所有参数
if (!(o instanceof ServletRequest) && !(o instanceof MultipartFile)
&& !(o instanceof ServletResponse) && null != o) {
params.append(o);
}
}
}
if (StringUtils.isNotBlank(cacheTarget.keyHead())) {
String key = cacheTarget.keyHead();
if (cacheTarget.ifAddParams().equals(CacheParams.ifAddParams)) {
key = key + params;
}
if (cacheTarget.isHash().equals(CacheParams.isHash)) {
return hash(key);
}
return key;
}
Class<?> clazz = joinPoint.getTarget().getClass();
String className = clazz.getName();
String methodName = joinPoint.getSignature().getName();
StringBuilder sb = new StringBuilder();
sb.append(className).append(methodName).append(params);
String key = hash(String.valueOf(sb));
// log.warn(“getkey className-[{}]-methodName-[{}]-params-[{}]-key-[{}]”,
// className, methodName, params, key);
return RedisKey.CACHE_INTERFACE + key;
}
/**
- 三重hash 防止碰撞
- @param sb 素值
- @return hash值
*/
private String hash(String sb) {
StringBuilder stringBuilder = new StringBuilder(sb);
return stringBuilder.toString().trim().hashCode() + “-” +
String.valueOf(stringBuilder.append(SALT)).trim().hashCode() + “-” +
String.valueOf(stringBuilder.append(SALT).append(SALT)).trim().hashCode();
}
/**
- 缓存内部参数
- @param joinPoint aop切面入参
- @return 缓存超时时间
*/
public CacheTarget getcacheTarget(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CacheTarget cacheTarget = method.getAnnotation(CacheTarget.class);
return cacheTarget;
}
}
class CacheParams {
// 默认缓存时间1秒 最大2天
public final static String time = String.valueOf(24 * 60 * 60 * 2);
// 参数是否加入到key中 1是 2否
public final static String ifAddParams = “1”;
// key是否hash 1是 2否 如果需要类或者方法体来做key默认必须hash
public final static String isHash = “1”;
// 是否刷新缓存 1是 2否
public final static String refresh = “1”;
}
切面tag
package web.m.reporter.common.cache;
import java.lang.annotation.*;
/**
-
缓存标签
-
-
auth:fengzk
-
date:2022/04/19
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
public @interface CacheTarget {
// 默认缓存时间1秒 最大2天 TODO 结合缓存刷新使用 如果缓存刷新介入当前接口缓存时效不起作用
String time() default “1”;// 指定的key头 和 当前类方法不可共存
String keyHead() default “”;// 参数是否加入到key中 1是 2否
String ifAddParams() default “1”;// key是否hash 1是 2否 如果需要类或者方法体来做key默认必须hash
String isHash() default “2”;// 是否刷新缓存 1是 2否
String refresh() default “2”;
}
缓存刷新工具
package web.m.reporter.common.cache;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.Gcache;
import web.m.reporter.common.RedisKey;
import web.m.reporter.util.TimeUtil;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
-
@author fengzekun
-
@version 1.0
-
@date 2022/4/20 18:55
-
@description: 缓存刷新
*/
@Slf4j
@Component
public class ReFresh {
@Autowired
private Gcache gcache;private static ConcurrentHashSet points = new ConcurrentHashSet<>();
private static ConcurrentHashSet<Pair<Date, ProceedingJoinPoint>> joinPoints =
new ConcurrentHashSet<>();@Autowired
private CacheHandlerAspect cacheHandlerAspect;public ReFresh() {
}public void reFresh() {
Iterator<Pair<Date, ProceedingJoinPoint>> iterator = joinPoints.iterator();
while (iterator.hasNext()) {
Pair<Date, ProceedingJoinPoint> freshVo = iterator.next();
String key = RedisKey.REFRESH_CACHE_KEY + freshVo.getValue().hashCode();
if (freshVo.getKey().after(TimeUtil.getTimeByStr(TimeUtil.getTimeStrByTime(new Date(),
TimeUtil.format1), TimeUtil.format1))) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
if (!getRefreshFactor(key)) {
return;
}
cacheHandlerAspect.getDateHandler(freshVo.getValue(), Boolean.FALSE);
} catch (Throwable throwable) {
log.error(“刷新缓存因子异常” + JSONObject.parseObject(
freshVo.getValue().getSignature().getName()));
} finally {
gcache.del(key);
}
}
};
threadPoolExecutor.execute(runnable);
}
}
delTask();
}private Boolean getRefreshFactor(String key) {
return gcache.setnxex(key,
RedisKey.REFRESH_CACHE_KEY_LIFE, “1”) > 0 ?
Boolean.TRUE : Boolean.FALSE;
}public void addTask(ProceedingJoinPoint joinPoint) {
String key = cacheHandlerAspect.getCacheKey(joinPoint,
cacheHandlerAspect.getcacheTarget(joinPoint));
if (!points.contains(key)) {
points.add(key);
joinPoints.add(new Pair<>(new Date(), joinPoint));
}
}private void delTask() {
Iterator<Pair<Date, ProceedingJoinPoint>> iterator = joinPoints.iterator();
while (iterator.hasNext()) {
Pair<Date, ProceedingJoinPoint> freshVo = iterator.next();
if (freshVo.getKey().before(TimeUtil.getTimeByStr(TimeUtil.getTimeStrByTime(new Date(),
TimeUtil.format1), TimeUtil.format1))) {
iterator.remove();
points.remove(cacheHandlerAspect.getCacheKey(freshVo.getValue(),
cacheHandlerAspect.getcacheTarget(freshVo.getValue())));
}
}
}public void clear() {
gcache.del(points.stream().map(p->p).collect(Collectors.joining()));
points.clear();
joinPoints.clear();
log.info(“缓存清理完成”);
}final Integer CPU_NUM = 5;
private final ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
CPU_NUM,
10 * CPU_NUM,
1000L * 50,
TimeUnit.MILLISECONDS,
new BlockingArrayQueue<>(10),
new ThreadFactoryBuilder().setNameFormat(“REFRESH_CACHE_%d”).build(),
new ThreadPoolExecutor.DiscardOldestPolicy());
}