AOP记录访问量

简介

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

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

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

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

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

  • hash,link:visit:$id

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

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

开始吧

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

注解类

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 访问量记录,适合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天,不然就不对了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @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了😂

查询访问量

没什么说的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@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实现的很不成熟,实战意义不大。

哈哈哈哈哈。。。。。