简介
最近在做一个短链接的项目,其实应该是算改造,将原来的功能完善一下,其中就包括一个记录访问量的需求,需求大概是这样的:
根据每个链接记录其一定时间内的访问量,这个访问量应该是随时间动态变化,即使我不做什么操作。
最容易就想到利用Redis的两种数据结构string和hash
- 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;
}
切面
前面也提到了,这个只适用于第一个参数是需要记录访问量的唯一id,Redis操作也是相当简单,”前缀+id+时间“为key,value为数量就完事了
还有一个很重要的,这里设置的过期时间一直要和之后需要查询时间对应,也就是说设置了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实现的很不成熟,实战意义不大。
哈哈哈哈哈。。。。。