h5制作软件app手机版,深圳债务优化公司,网站建设推广公司排名,如何加强省市级政府门户网站建设导读
在日常使用中#xff0c;我们经常能看见内容推送功能。
常见的场景有#xff0c;比如你在bilibili关注了某个up主#xff0c;当up主发布视频后#xff0c;就会推送到你的收件箱或者是动态中#xff0c;让粉丝能够及时得知所关注的人发布了内容。
又比如朋友圈我们经常能看见内容推送功能。
常见的场景有比如你在bilibili关注了某个up主当up主发布视频后就会推送到你的收件箱或者是动态中让粉丝能够及时得知所关注的人发布了内容。
又比如朋友圈也是按照时间的顺序将好友发布的动态推送给你如果你下拉刷新就可以获取到新的好友的动态。
想知道这些功能是如何实现的吗接着往下看吧
这个需求其实我们又把他叫做Feed流关注推送也叫做Feed流直译为投喂。为用户持续的提供“沉浸式”的体验通过无限下拉刷新获取新的信息。
对于传统模式的内容解锁我们是需要用户去通过搜索引擎或者是其他的方式去解锁想要看的内容 而对于Feed模式则是主动推送给用户内容 Feed流的两种模式
Feed流的实现有两种模式
Timeline不做内容筛选简单的按照内容发布时间排序常用于好友或关注。例如朋友圈 优点信息全面不会有缺失。并且实现也相对简单 缺点信息噪音较多用户不一定感兴趣内容获取效率低
智能排序利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户 优点投喂用户感兴趣信息用户粘度很高容易沉迷 缺点如果算法不精准可能起到反作用
我们本次关注-推送的功能采用的就是Timeline的方式只需要拿到我们关注用户的信息然后按照时间排序即可。
该模式的实现方案有三种拉模式推模式推拉结合
拉模式读扩散
该模式的核心含义就是当张三和李四和王五发了消息后都会保存在自己的邮箱中假设赵六要读取信息那么他会从读取他自己的收件箱此时系统会从他关注的人群中把他关注人的信息全部都进行拉取然后在进行排序
优点比较节约空间因为赵六在读信息时并没有重复读取而且读取完之后可以把他的收件箱进行清除。
缺点比较延迟当用户读取数据时才去关注的人里边去读取数据假设用户关注了大量的用户那么此时就会拉取海量的内容对服务器压力巨大。 推模式写扩散
推模式是没有写邮箱的当张三写了一个内容此时会主动的把张三写的内容发送到他的粉丝收件箱中去假设此时李四再来读取就不用再去临时拉取了
优点时效快不用临时拉取
缺点内存压力大假设一个大V写信息很多人关注他 就会写很多分数据到粉丝那边去 推拉结合模式
推拉模式是一个折中的方案站在发件人这一段如果是个普通的人那么我们采用写扩散的方式直接把数据写入到他的粉丝中去因为普通的人他的粉丝关注量比较小所以这样做没有压力如果是大V那么他是直接将数据先写入到一份到发件箱里边去然后再直接写一份到活跃粉丝收件箱里边去现在站在收件人这端来看如果是活跃粉丝那么大V和普通的人发的都会直接写入到自己收件箱里边来而如果是普通的粉丝由于他们上线不是很频繁所以等他们上线时再从发件箱里边去拉信息。 Redis实现思路
我们使用Redis来实现Feed流就需要选择合适的数据结构。
Feed流有两种特点一是需要进行时间排序二是数据不断在更新变化即角标不断变化。
可以进行排序的数据结构有listsortedset都满足第一点需求。
feed流推送过来的内容我们通常不是一次性查询所有内容而是需要分页查询。传统的分页查询使用page和size来圈定范围但是feed流不断进行内容推送数据不断进行更新类似栈结构时间戳越大的在上头数据角标不断变化使用传统的分页模式就会导致内容的重复查询。所以需要使用滚动查询滚动查询指的是记录上一次所查询的位置下次查询时接着往下查询。所以使用sortedset结构我们可以记录每次查询最小的时间戳下次查询的时候再找比这个时间戳更小的就实现了滚动查询的效果。而list结构只能通过角标查询。所以我们最终选择sortedset结构。 流程
1.保存笔记并推送
在用户发布笔记保存到数据库的同时也需要将笔记id推送到粉丝的收件箱中即sortedSet集合中。key是“feed:粉丝id”value是blog的idscore是当前时间戳
savaBlog 验证用户是否登录 验证blog是否完整发布者id内容关联商户 保存blog到数据库 推送blog给粉丝 获取登录用户粉丝集合 循环遍历每个粉丝保存blogId到粉丝sortedset集合 返回blog的id
2.获取推送笔记
如何使用Redis的sortedSet结构来实现滚动分页获取推送的笔记内容
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
命令解释根据score值进行降序排序查询count条集合中score范围为max到min的值 key键 maxmin查询max到min范围的值 [WITHSCORES ]是否带上score值 offset偏移量表示对于max值的偏移量0表示对max偏移量为0即取max值 count查询几条
实现滚动分页我们将时间戳作为score值对score值进行降序排序就可以根据发布时间对笔记进行排序。
滚动分页的基本思想是记录上一次查询的最小时间戳下次查询时从所记录的位置开始查询。如果是第一次查询max值就是当前时间戳offset值取0min值取0因为时间戳最小为0不可能为负数如果不是第一次查询max值就是上一次查询的最小时间戳offset值取1即不包括上一次查询的位置否则会重复查询min值取0。
需要注意的是offset的值第一次查询时为0之后的查询不一定为1因为有可能会出现时间戳相等的情况
比如score值降序排序后为5 2 2 3 2 2每次查询3条记录。
那么第一次所查到的score是5 2 2此次记录的最小时间戳是2。下次查询时redis会查找到score值为2的位置即第一个2然后根据偏移量进行便宜如果偏移量为1那么结果就会为 2 3 2 而不是 3 2 2。所以偏移量offset的值应该是当前查询最小时间戳的重复次数。 所以通过上面的分析我们发现min和count值是不变的。max和offset都需要通过前端作为请求参数传递。
当第一次查询时前端发送的请求参数max为当前时间戳offset默认为0可以不传递当后端查询出所需要的笔记集合还需要记录本次查询的最小时间戳以及计算偏移量并返回给前端这两个数据将作为下一次查询的参数。而前端每次上下拖动的时候就会刷新会发起请求。
queryBlogOfFollowmaxoffset: 检验是否登录获取当前登录用户 查看收件箱 滚动查询收件箱获取笔记id 根据笔记id查询笔记 封装返回 Overridepublic Result queryBlogOfFollow(Long max, Integer offset) {//获取当前用户Long userId UserHolder.getUser().getId();//查看收件箱String key FEED_KEY userId;SetZSetOperations.TypedTupleString typedTuples stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);//非空校验if(typedTuples null || typedTuples.isEmpty()){return Result.ok();}//取出收件箱的笔记id放入集合ListLong idList new ArrayList(typedTuples.size());long minTime 0;int os 1;for (ZSetOperations.TypedTupleString typedTuple : typedTuples) {idList.add(Long.valueOf(typedTuple.getValue()));//获取最小的时间戳记录偏移量long time typedTuple.getScore().longValue();if(time minTime){os;}else{minTime time;os 1;}}//根据id集合查询blogString idStr StrUtil.join(,,idList);ListBlog blogs query().in(id, idList).last(ORDER BY FIELD(id, idStr )).list();//查询blog点赞和相关用户for(Blog blog : blogs){queryBlogUser(blog);isBlogLiked(blog);}//封装并返回ScrollResult result new ScrollResult();result.setList(blogs);result.setOffset(os);result.setMinTime(minTime);return Result.ok(result);}