黑马点评-Feed流的实现方案,基于推拉结合模式实现笔记推送

Feed流实现方案

我们关注了博主之后,当用户发布了动态后我们应该把这些数据推送给粉丝,关注推送也叫作Feed(投喂)流,通过无限下拉刷新获取新的信息

  • 传统的模式内容检索: 粉丝需要主动通过搜索引擎或者是其他方式去查找想看的内容
  • 新型Feed流的效果: 系统分析用户到底想看什么,然后主动把内容推送给用户, 不需要用户主动去搜索资源节约用户时间

Feed流实现的两种模式

Timeline: 对发布的信息不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注(朋友圈等)

  • 优点:信息全面不会有缺失并且实现也相对简单

  • 缺点:信息噪音较多用户不一定感兴趣并且内容获取效率低

智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容,推送用户感兴趣的信息来吸引用户

  • 优点: 投喂用户感兴趣的信息,用户粘度很高,容易沉迷
  • 缺点: 如果算法不精准可能会起到反作用即给用户推了不想看的内容

个人页面一般就是基于关注的好友来做Feed流,因此采用Timeline方式只需要拿到我们关注用户的发布的内容然后按照时间排序即可

在这里插入图片描述

拉模式也叫读扩散,每个用户都有自己的发件箱和收件箱

  • 优点节约空间:系统只会将用户关注的博主发布的动态放入到用户的收件箱,用户每次从自己的收件箱中读取信息
  • 缺点拉取信息有延迟:只要用户读取数据时就会去他关注的所有人的发件箱中拉取信息,假设该用户关注了海量用户此时就会拉取很多信息对服务器压力巨大

在这里插入图片描述

推模式也叫写扩散,推模式没有发件箱

  • 优点时效快: 系统主动将博主发布的动态推送到其粉丝的收件箱中,这样用户每次读取信息时就不需要临时拉取
  • 缺点: 内存压力大,假设一个大V发了一个动态此时就会写很多份数据发到他的粉丝收件箱中

在这里插入图片描述

推拉结合也叫读写混合,兼具推和拉两种模式的优点,只要大v才有发件箱

在这里插入图片描述

推送笔记ID到粉丝收件箱

需求: 修改新增探店笔记的业务,在保存Blog对象到数据库的同时推送笔记到粉丝的收件箱

  • 实现收件箱功能: 收件箱要求满足可以根据时间戳对数据排序(降序),同时用户查询收件箱数据时也可以根据角标实现分页查询

传统的分页模式: 查询数据时要求数据库中的数据角标必须固定,否则可能会出现数据重复读取的情况

在这里插入图片描述

Feed流的滚动分页: 记录每次查询操作的最后一条数据,下次查询时会从上次读取的最后一条数据之后开始读取数据

  • List集合: 只能按照角标查询所以不支持滚动分页
  • SortedSet集合: 可以按照集合中元素的score值的范围进行查询,我们每次查询的时候可以记录查询到笔记Id的最小时间戳

在这里插入图片描述

第一步: 修改BlogController中保存笔记的方法,当博主发布完探店笔记后还要将发布笔记的Id推送到所有粉丝的收件箱中,score值是当前的时间戳(默认升序)

// 在RedsiConstants类声明一个常量作为用户收件箱的前缀
public static final String FEED_KEY = "feed:";
@Resource
private IFollowService followService;
@Override
public Result saveBlog(Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文boolean isSuccess = save(blog);if(!isSuccess){return Result.fail("新增笔记失败");}// 条件构造器LambdaQueryWrapper<Follow> queryWrapper = new LambdaQueryWrapper<>();// 从follow表中查找博主的所有粉丝select * from follow where follow_user_id = user_idqueryWrapper.eq(Follow::getFollowUserId, user.getId());List<Follow> follows = followService.list(queryWrapper);for (Follow follow : follows) {// 获取粉丝IdLong userId = follow.getUserId();// 推送发布笔记的Id到每个粉丝的收件箱(score值是当前时间戳),每个粉丝都有一个自己的收件箱String key = FEED_KEY + userId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());}// 返回笔记的idreturn Result.ok(blog.getId());
}

查询推送笔记

需求: 在个人主页的关注栏中查询并展示推送的Blog信息

ZREVRANGEBYSCORE key 最大值范围 最小值范围 WITHSCORES LIMIT offSet(偏移量) 查询到的个数: 获取指定的score范围内的元素并按照降序排序

  • 最小时间戳: 每次分页查询完成之后要记录查询的最小时间戳,将这个最小时间戳作为下一次分页查询的条件
  • 编译量: 设置要从上次查询的最大值后面跳过几个元素,0表示不跳过元素,1表示跳过一个元素,偏移量的值取决于当前集合内有几个元素和上次查询到的最大值相同

在这里插入图片描述

第一步: 业务中不一定只对Blog进行分页查询,可以使用泛型做一个通用的分页查询

@Data
public class ScrollResult {// 封装查询到的数据private List<?> list;// 记录本次查询的最小时间戳,作为下一次查询的最大值(起始值)private Long minTime;// 记录偏移量private Integer offset;
}

第二步: 在BlogController中创建对应的分页查询方法, 具体实现逻辑在BlogServiceImpl中完成

@GetMapping("/of/follow")
// 由于第一次查询的时候没有传递offset参数,可以设置默认值为0
public Result queryBlogOfFollow(@RequestParam("lastId") Long max, @RequestParam(value = "offset",defaultValue = "0") Integer offset){return blogService.queryBlogOfFollow(max,offset);
}
@Override
public Result queryBlogOfFollow(Long max, Integer offset) {//1. 获取当前用户的IdLong userId = UserHolder.getUser().getId();//2. 查询该用户的收件箱获取该用户对应的SortedSet集合中所有的笔记Id及score值看是否有关注的博主发了笔记String key = FEED_KEY + userId;Set<ZSetOperations.TypedTuple<String>> typeTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);//3. 非空判断if (typeTuples == null || typeTuples.isEmpty()){return Result.ok(Collections.emptyList());}//4. 获取SortedSet集合中所有的笔记Id及score值,List集合的大小和Set集合一致,可以略微提高效率避免长度重置ArrayList<Long> ids = new ArrayList<>(typeTuples.size());// 保存最小的时间戳long minTime = 0;// 记录最小时间戳的个数即偏移量int os = 1;for (ZSetOperations.TypedTuple<String> typeTuple : typeTuples) {//4.1 获取推送的Blog的IdString id = typeTuple.getValue();ids.add(Long.valueOf(id));//4.2 将笔记对应的score(时间戳)转换为long类型long time = typeTuple.getScore().longValue();if (time == minTime){// 如果当前的时间戳等于最小时间戳则最小时间戳个数+1os++;}else {// 如果当前时间戳不等于最小时间戳,则把当前时间作为最小时间戳同时把最小时间戳的个数重置为1minTime = time;os = 1;}}// 解决MySQL的in语句的自动按照id大小的排序问题,手动指定排序方式为传入的ids集合中的顺序String idsStr = StrUtil.join(",");//5. 根据推送的ids集合查询所有的blogList<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idsStr + ")").list()for (Blog blog : blogs) {//5.1 查询发布该blog的用户信息queryBlogUser(blog);//5.2 查询当前用户是否给该blog点过赞isBlogLiked(blog);}//6. 封装结果并返回ScrollResult scrollResult = new ScrollResult();scrollResult.setList(blogs);scrollResult.setOffset(os);scrollResult.setMinTime(minTime);return Result.ok(scrollResult);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/170740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UGUI】中Content Size Fitter)组件-使 UI 元素适应其内容的大小

官方文档&#xff1a;使 UI 元素适应其内容的大小 - Unity 手册 必备组件&#xff1a;Content Size Fitter 通常&#xff0c;在使用矩形变换定位 UI 元素时&#xff0c;应手动指定其位置和大小&#xff08;可选择性地包括使用父矩形变换进行拉伸的行为&#xff09;。 但是&a…

突破技术障碍:软件工程师如何应对项目中的难题?

在软件开发项目中&#xff0c;工程师常常会遇到各种技术难题。这些难题可能涉及到复杂的算法、不兼容的系统、难以预见的软件行为&#xff0c;或者其他许多方面。 以下是一些策略和方法&#xff0c;可以帮助软件工程师有效地应对这些挑战&#xff1a; 1、理解问题&#xff1a;…

count=0语句的位置

简洁一点的代码&#xff1a; 像count0这种语句要注意放好位置&#xff0c;尤其是在循环里。

SAP Smartform小结

SAP系统做打印单据用的, 感觉很不好用, 特别是要嵌入韩文时必须使用嵌入的word编辑器,运行速度简直不可忍受. 见过一些Adobe interactive form的示例, 看着相当不错, 不过据说需要花money额外买licence, 哪有smartform这种免费东西来得实惠. 一般打印需求,会要求有标题抬头,打…

TikTok 将开源“云中和”边缘加速器

“从某种意义上说&#xff0c;我们正在努力破解云的骨干网&#xff0c;以造福于我们&#xff0c;”TikTok产品管理基础设施经理Vikram Siwach指出&#xff0c;他解释了该公司即将开源的“全球服务加速器”的好处&#xff0c;这是一个可编程的边缘平台&#xff0c;可将应用程序需…

Linux常用命令——bg命令

在线Linux命令查询工具 bg 用于将作业放到后台运行 补充说明 bg命令用于将作业放到后台运行&#xff0c;使前台可以执行其他任务。该命令的运行效果与在指令后面添加符号&amp;的效果是相同的&#xff0c;都是将其放到系统后台执行。 在Linux系统中执行某些操作时候&…

【通讯协议】gRPC和Webhook

RPC&#xff08;Remote procedure Call&#xff09;之所以被称为“远程”&#xff0c;是因为在微服务架构下&#xff0c;当服务部署到不同的服务器上时&#xff0c;它可以实现远程服务之间的通信。从用户的角度来看&#xff0c;它的作用就像本地函数调用。 下图说明了gRPC的整…

【漏洞复现】Array VPN任意文件读取漏洞

漏洞描述 华耀(中国)科技有限公司(简称:Array)于2003年创建于北京,是优秀的网络功能平台解决方案提供商,也是应用交付解决方案、移动应用接入(SSL VPN)解决方案的全球领导者。华耀现有员工200余人,其中研发团队占到100余人,总部位于北京。并在北京、上海、广州、杭…

文件属性和路径

文件属性 我们先补充一个知识&#xff1a; 任何新建的文件刚开始都是在磁盘上的 假设我们在文件夹中新建一个1.txt文本文档&#xff0c;他的大小是0KB&#xff0c;但是不能说明这个文件不占用磁盘空间 文件由 文件属性文件内容 组成&#xff0c;这里只能说明文件内容是空 文件…

美国季节性干旱数据集

美国季节性干旱数据集 美国干旱展望栅格数据集由国家气象局气候预测中心生成。它在每个月的最后一天发布&#xff0c;提供下个月的干旱前景信息。“美国季节性干旱展望”数据集每月发布一次&#xff0c;特别是每月的第三个星期四。该数据集对美国不同地区发生干旱的可能性进行…

MySQL性能优化,SQL调优,SQL调优的手段

文章目录 对MySQL性能的优化的理解硬件和操作系统层面的优化架构设计层面的优化MySQL程序配置优化SQL优化 SQL调优有哪几种方式1.EXPLAIN2.SQL语句中IN包含的值不应过多3.SELECT语句务必指明字段名称4.当只需要一条数据的时候&#xff0c;使用limit 15.如果排序字段没有用到索引…

04 _ 系统设计目标(二):系统怎样做到高可用?

这里将探讨高并发系统设计的第二个目标——高可用性。 高可用性&#xff08;High Availability&#xff0c;HA&#xff09;是你在系统设计时经常会听到的一个名词&#xff0c;它指的是系统具备较高的无故障运行的能力。 我们在很多开源组件的文档中看到的HA方案就是提升组件可…

调试器gdb

目录 一、调试 1、前言 2、 debug和release 二、基本操作 1、退出 quit 2、开始调试 r 3、打断点 b 4、查看断点 info b 5、查看代码 l 6、删除断点 d 7、逐过程 n 8、打印变量内容 p 9、逐语句&#xff08;进入函数&#xff09; s 10、查看函数调用堆栈 bt 11、…

linux rpm安装软件卸载 以卸载mysql为例

查看rpm包 rpm -qa | grep 内容 卸载rpm rpm -e --nodeps rpm名称

【Unity实战】切换场景加载进度和如何在后台异步加载具有庞大世界的游戏场景,实现无缝衔接(附项目源码)

文章目录 最终效果前言一、绘制不同的场景二、切换场景加载进度1. 简单实现2. 优化 三、角色移动和跳跃控制四、添加虚拟摄像机五、触发器动态加载场景六、最终效果参考源码完结 最终效果 前言 观看本文后&#xff0c;我的希望你对unity场景管理有更好的理解&#xff0c;并且能…

辅助驾驶功能开发-外部需求篇-导航式巡航辅助NCA外部依赖及需求

1.功能外部依赖 为了设计满足用户需求的 ICA/NCA 系统,需要用到的系统输入为:环境及驾驶员感知信息、 门锁监测信息、胎压监测信息、驾驶员权限管理、车辆模式管理信息、用户请求输入、转向系统 信息、惯性及导航定位信息、被动安全及约束信息、变速箱系统信息、制动系…

华为云之在Linux系统下安装可视化界面

华为云之在Linux系统下安装可视化界面 一、华为云弹性云服务器ECS介绍二、Linux图形化界面介绍三、本次实践介绍3.1 本次实践简介3.2 本次实践环境介绍 四、环境准备工作4.1 预置环境4.2 查看预置环境资源信息 五、连接弹性云服务器ECS5.1 登录华为云5.2 复制ECS弹性公网IP地址…

云计算学习哪些技术

1. 虚拟化技术 虚拟化是云计算的基础&#xff0c;因此了解虚拟化技术至关重要。学习虚拟化技术时&#xff0c;需要掌握以下知识点&#xff1a; - 操作系统虚拟化&#xff1a;了解基于宿主机和客户机的虚拟化技术&#xff0c;如VMware、Hyper-V等。 - 容器化技术&#xff1a;学习…

php的字符转义函数有那些,是干什么的

在 PHP 中&#xff0c;字符转义函数是用于处理字符串中的特殊字符&#xff0c;以防止这些字符被误解、滥用或引起安全问题的一组函数。这些函数的主要作用是确保在将用户提供的数据插入到数据库、构建 HTML 输出或进行其他与安全相关的操作时&#xff0c;不会导致潜在的安全漏洞…