AOP记录访问量

99 阅读2分钟

简介

最近在做一个短链接的项目,其实应该是算改造,将原来的功能完善一下,其中就包括一个记录访问量的需求,需求大概是这样的:

根据每个链接记录其一定时间内的访问量,这个访问量应该是随时间动态变化,即使我不做什么操作。

最容易就想到利用Redis的两种数据结构stringhash

  • string,link:visit:$id:$date

需要通过时间来设置和查询,可单独设置过期时间,删除不能直接通过link来删除,只能自然过期?

  • hash,link:visit:$id

设置和查询简单,不可单独设置过期时间,删除可一次性删除

当然我还看到有利用hyperloglog记录访问集合的,这与我需求不符就不提了

开始吧

如标题所言,利用的就是AOP实现的

注解类

这种做法是有限制了,我也在下面写明了

/**
 * 访问量记录,适合get接口,且只有一个参数
 *
 * @author wnhyang
 * @date 2023/3/11
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Visit {
    /**
     * 描述
     *
     * @return String
     */
    String value();

    /**
     * 缓存前缀
     *
     * @return String
     */
    String prefix();

    /**
     * 缓存过期时间
     *
     * @return long
     */
    long timeout() default 7L;

    /**
     * 缓存过期时间单位
     *
     * @return TimeUnit
     */
    TimeUnit unit() default TimeUnit.DAYS;
}

切面

前面也提到了,这个只适用于第一个参数是需要记录访问量的唯一idRedis操作也是相当简单,”前缀+id+时间“为keyvalue为数量就完事了

还有一个很重要的,这里设置的过期时间一直要和之后需要查询时间对应,也就是说设置了7天,之后查的也只能是7天,不然就不对了

/**
 * @author wnhyang
 * @date 2023/3/11
 **/
@Aspect
@Slf4j
@Component
@RequiredArgsConstructor
public class VisitAspect {

    private final ValueOperations<String, String> valueOperations;

    @After("@annotation(visit)")
    public void after(JoinPoint point, Visit visit) {
        log.info("value {},prefix {},timeout {},unit {}", visit.value(), visit.prefix(), visit.timeout(), visit.unit());
        Object[] args = point.getArgs();
        String now = LocalDate.now().toString();
        // args这么放,那么就不具备普适性了
        String key = RedisUtils.getKey(visit.prefix(), args[0], now);
        Object value = valueOperations.get(key);
        if (null != value) {
            valueOperations.increment(key);
        } else {
            valueOperations.set(key, "1", visit.timeout(), visit.unit());
        }
    }
}

放弃

也是因为上面说的那些缺点,最终还是放弃了这个方法

改回了每次访问主动地去操作redis了😂

查询访问量

没什么说的

@Override
public Visits getVisits(Long id) {
  return getVisits(id, RedisUtils.TIME_OUT_30);
}

@Override
public Visits getVisits(Long id, long days) {
  LinkMap linkMap = baseMapper.selectById(id);
  if (null == linkMap) {
    return null;
  }
  LocalDate now = LocalDate.now();
  Visits visits = new Visits();
  int total = 0;
  List<VisitsVO> visitsVOList = new ArrayList<>();
  for (int i = 0; i < days; i++) {
    String date = now.minusDays(i).toString();
    String key = RedisUtils.getKey(CacheConstants.LINK_VISITS, id, date);
    String count = valueOperations.get(key);
    if (null == count || "0".equals(count)) {
      continue;
    }
    total += Integer.parseInt(count);
    VisitsVO visitsVO = new VisitsVO();
    visitsVO.setDate(date);
    visitsVO.setCount(count);
    visitsVOList.add(visitsVO);
  }
  visits.setTotal(total);
  visits.setVisitsVOList(visitsVOList);
  return visits;
}

总结

说到底,AOP实现的很不成熟,实战意义不大。

哈哈哈哈哈。。。。。