拦截器缓存+缓存刷新工具

项目启动后启动一个线程不断刷新
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());

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值