Feed流推送之订阅推送
分类
feed流分为TimeLine和智能排序,前者不对内容进行过滤,一般根据发布的时间来进行排序,一般用于好友动态或者推送关注的人的消息,而后者一般有着复杂的算法,可以根据算法智能地向目标用户推送内容,例如抖音的推送算法。
方案
Feed流推送一般有着三种方案
- 推模式,即发布者一有新消息,则将消息全部发送到粉丝的收件箱中,优点延迟小,缺点内存占用高
- 拉模式,即发布者将新消息发送到自己的发件箱,用户上线后从所有关注的人的列表中拉取新消息,优点占用内存小,缺点是读延迟高。
- 推拉结合的混合模式,通过推拉结合,根据发布者或者粉丝的分类进行合适的推拉模式选择,例如大V的粉丝数量庞大,对于他的活跃粉丝,采用推模式,对于普通粉丝,采用拉模式
在黑马点评的feed推送关注的博主的新发布博客中,一般用户的粉丝数不会特别大,采用推模式来实现,基于redis来实现推模式。
方案的实现
数据结构的选择:用户的收件箱应该根据用户关注的博主发布的博客的时间戳来进行排序,而redis中能够进行排序的有两种数据结构,分别为list和SortedSet, list为传统角标排序,而sortedSet能够根据给定的值来排序,这里用sortedset数据结构,根据关注的博主发布的博客的时间戳来实现
思路:
1.获取当前用户
- 获取redis中的收件箱,并判空
- 解析收件箱中的博客,解析出博客的id和时间戳
- 根据id查找博客
- 封装返回
粉丝查询代码:
/*** 滚动分页查询关注的人的博客,从时间为max前的第offset条开始查询* @param max * @param offset * @return*/
@Override
public Result queryBlogOfFollow(Long max, Integer offset) {//获取当前用户Long userId = UserHolder.getUser().getId();//获取redis中的收件箱String key = RedisConstants.FEED_KEY+userId;Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);if(typedTuples == null || typedTuples.isEmpty()){return Result.ok();}//解析博客List<Long> Ids = new ArrayList<>(typedTuples.size());long timeMin = 0;int count = 1;for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {//获取idString str = typedTuple.getValue();if(str!=null) Ids.add(Long.valueOf(str));//获取时间戳Long time = typedTuple.getScore().longValue();if(Objects.equals(time, timeMin)){count++;}else{count = 1;}timeMin = time;}//解析id,根据id查找博客String idsStr = StrUtil.join(",",Ids);List<Blog> blog = blogService.query().in("id", Ids).last("ORDER BY FIELD(id," + idsStr + ")").list();for (Blog blog1 : blog) {queryBlogUser(blog1);isBlogLiked(blog1);}//封装返回ScrollResult scrollResult = new ScrollResult();scrollResult.setList(blog);scrollResult.setMinTime(timeMin);scrollResult.setOffset(count);return Result.ok(scrollResult);
}
发布者代码:
/*** 保存博客* @param blog* @return*/
@Override
public Result saveBlog(Blog blog) {
// 获取登录用户
UserDTO user = UserHolder.getUser();
blog.setUserId(user.getId());
// 保存探店博文
boolean success = blogService.save(blog);
if(!success){return Result.fail("发布失败");
}
/*
将消息推送到所有粉丝
*/
//获取粉丝
List<Follow> followUser = followService.query().eq("follow_user_id", user.getId()).list();
//推送消息
for(Follow follow:followUser){Long blogId = blog.getId();stringRedisTemplate.opsForZSet().add(RedisConstants.FEED_KEY+follow.getUserId() ,blogId.toString() ,System.currentTimeMillis());
}
// 返回id
return Result.ok(blog.getId());
}