1.热点文章整体思路

image-20210914095134883.png

  • 定时计算热点文章
    • 定时任务每天凌晨1点,查询前5天的文章
    • 计算每个文章的分值,其中不同的行为设置不同的权重(阅读:1,点赞:3,评论:5,收藏:8)
    • 按照分值排序,给每个频道找出分值较高的30条数据,存入缓存中为什么要按照频道缓存?
  • 实时采集文章行为
    • 行为微服务,用户阅读或点赞了某一篇文章(目前实现这两个功能),发送消息给rabbitMQ
    • 文章微服务,接收行为消息,使用redis中list结构存储实时的行为数据
  • 定时更新热度值
    • 文章微服务,接收聚合之后的消息,计算文章分值(当日分值计算方式,在原有权重的基础上再*3)
    • 根据当前文章的频道id查询缓存中的数据
    • 当前文章分值与缓存中的数据比较,如果当前分值大于某一条缓存中的数据,则直接替换
    • 新数据重新设置到缓存中
    • 更新数据库文章的行为数量
  • 查询热点文章列表

    • 判断是否是首页
    • 是首页,选择是推荐,tag值为all,从所有缓存中筛选出分值最高的30条数据返回
    • 是首页,选择是具体的频道,tag是具体的数字,从缓存中获取对应的频道中的数据返回
    • 不是,则查询数据库中的数据

      2.文章分值定时计算

    • 所用技术: 分布式任务调度 XXL-Job 根据Corn表达式完成定时任务

      Redis 操作以后数据都存入Redis中保存

    • 实现思路

image.png

  1. 1. 首先查询前5天的 (已上架、未删除) 文章数据
  2. 1. 计算所查询所有文章的热度值,根据权重计算分值
  3. 1. 为每一个频道缓存热点较高的30条文章,若为推荐频道则缓存所有文章热度值排名前30的文章
  4. 1. 按照文章热度降序 排序 截取前30条文章

3.实时采集文章行为

  • 所用技术: Redis MQ
  • 实现思路: 因为之前已经有一个定时任务负责采集近期(5天内)的热点文章数据,但是当天也会有 用户对文章进行操作,阅读点赞等,所以需要利用MQ进行监控,一旦有用户对某个文章进 行了操作,就需要用MQ发送消息,将文章行为保存到Redis,再对数据做后续处理.

image-20210914150315840.png

  1. - 在文章的各种行为方法中利用MQ发消息,并且在Redis中缓存一个list用于存储文章的各种行为 例: {文章id,对于文章的操作行为,行为的增减量}

4.近实时更新文章热度分值(重要)

  • 所用技术: XXL-Job Redis MQ
  • 实现思路: 利用定时任务实现每过十秒检测一次Redis中是否有用户操作的数据,对于行为数据他 的发生频率非常高 可能1篇文章 最近10s被阅读了几万次,所以我们不可能每次都去 修改数据库,需要把最近一段时间内该文章的所有行为先进行统计,然后再更新文章 的相关热度信息

image.png

  1. 1. Redis中获取数据后,要对数据进行处理
  2. 1. 获取数据的总数,利用Redis的管道操作命令,分别读取指定长度的数据,而后删除取出的数据保留剩余数据,

image.png

  1. 2. 按照文章id对行为数据进行分组 [{articleId:1,add:1,type:view}{articleId:1,add:1,type:likes}] ==> {articleId:1, view:1,like:1}
  2. 1. 按照文章id分组 key: 文章id value: 文章行为数据List

key 文章id 1 value: List<> 3

  1. 2. 遍历分组 对每个分组进行聚合运算,每个分组统计出一个 ArticleVisitStreamMess.根据文章的状态,将文章中相同的操作进行聚合运算
  1. messMap.forEach((articleId,messList) -> {
  2. // 按照文章分组 对每个分组进行聚合运算 , 每个分组统计出一个 ArticleVisitStreamMess
  3. // 映射 将每一条行为数据 都转化为 ArticleVisitStreamMess聚合对象
  4. Optional<ArticleVisitStreamMess> reduce = messList.stream()
  5. .map(mess -> {
  6. ArticleVisitStreamMess articleVisitStreamMess = new ArticleVisitStreamMess();
  7. articleVisitStreamMess.setArticleId(articleId);
  8. switch (mess.getType()) {
  9. case LIKES:
  10. articleVisitStreamMess.setLike(mess.getAdd()); //点赞行为
  11. break;
  12. case VIEWS:
  13. articleVisitStreamMess.setView(mess.getAdd()); //阅读行为
  14. break;
  15. case COMMENT:
  16. articleVisitStreamMess.setComment(mess.getAdd()); //评论行为
  17. break;
  18. case COLLECTION:
  19. articleVisitStreamMess.setCollect(mess.getAdd()); //收藏行为
  20. break;
  21. }
  22. return articleVisitStreamMess;
  23. })
  24. .reduce(new BinaryOperator<ArticleVisitStreamMess>() {
  25. /**
  26. * 归并运算 将流中的数据进行两两运算
  27. * @param a1
  28. * @param a2
  29. * @return
  30. */
  31. @Override
  32. public ArticleVisitStreamMess apply(ArticleVisitStreamMess a1, ArticleVisitStreamMess a2) {
  33. a1.setView(a1.getView() + a2.getView());
  34. a1.setLike(a1.getLike() + a2.getLike());
  35. a1.setComment(a1.getComment() + a2.getComment());
  36. a1.setCollect(a1.getCollect() + a2.getCollect());
  37. return a1;
  38. }
  39. });
  1. 3. 修改文章分值 {articleId:1, view:1,like:1}
  2. 1. 根据 文章id查询出文章数据
  3. 1. 更新文章 各个行为的值
  4. 1. 计算文章得分
  5. 1. 判断文章是否是今日发布 如果是整体热度*3
  6. 1. 查询对应频道热点文章,替换分值较低文章
  7. 1. 重新对热点文章进行排序 并截取前30条热点文章
  8. 6. 查询推荐频道热点文章,替换分值较低文章

5.APP文章列表查询接口改造

  • 若APP查询的是第一页,那么就去Redis中查询热点文章,并返回
  • 若查询的不是第一页,则去数据库中查询普通文章