200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Redis——实现关注与取关等功能

Redis——实现关注与取关等功能

时间:2023-10-30 07:01:43

相关推荐

Redis——实现关注与取关等功能

目录

实现关注与取关

实现共同关注功能

关注推送

Feed流的模式

拉模式

推模式

推拉结合模式

总结

基于推模式实现关注推送功能

Feed流的分页问题

实现关注推送页面的分页查询

实现关注与取关

实现

Controller层

@RestController@RequestMapping("/follow")public class FollowController {@Resourceprivate IFollowService followService;@PutMapping("/{id}/{isFollow}")public Result follow(@PathVariable("id")Long followUserId,@PathVariable("isFollow") Boolean isFollow){return followService.follow(followUserId,isFollow);}@GetMapping("/or/not/{id}")public Result follow(@PathVariable("id")Long followUserId){return followService.isFollow(followUserId);}}

Service层

@Overridepublic Result follow(Long followUserId, Boolean isFollow) {//1.获取登录用户UserDTO user = UserHolder.getUser();Long userId = user.getId();//判断是取关还是关注if(isFollow){//2.关注,新增数据Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);follow.setCreateTime(LocalDateTime.now());boolean isSuccess = save(follow);}else{//取关,删除数据//sql: delete from tb_follow where user_id = ? and follow_user_id = ?remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));}return Result.ok();}@Overridepublic Result isFollow(Long followUserId) {//1.获取用户UserDTO user = UserHolder.getUser();Long userId = user.getId();//查询是否关注//sql: select * from tb_follow where user_id = ? and follow_user_idInteger count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();return Result.ok(count>0);}

实现共同关注功能

修改前面的关注接口,关注的对象不仅仅存入数据库中,还需存入redis中(共同关注功能需要set数据结构完成)

修改

@Overridepublic Result follow(Long followUserId, Boolean isFollow) {//1.获取登录用户UserDTO user = UserHolder.getUser();Long userId = user.getId();String key = "follows:"+userId;//判断是取关还是关注if(isFollow){//2.关注,新增数据Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);follow.setCreateTime(LocalDateTime.now());//修改部分boolean isSuccess = save(follow);if(isSuccess){//按关注用户的id放入redis的set集合中stringRedisTemplate.opsForSet().add(key,followUserId.toString());}}else{//取关,删除数据//sql: delete from tb_follow where user_id = ? and follow_user_id = ?//修改部分boolean isSuccess = remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_id", followUserId));if(isSuccess){stringRedisTemplate.opsForSet().remove(key,followUserId.toString());}}return Result.ok();}

实现

Controller层

@GetMapping("/common/{id}")public Result followCommons(@PathVariable("id")Long id){return followService.followCommons(id);}

Service层

@Overridepublic Result followCommons(Long id) {//1.获取当前用户idLong userId = UserHolder.getUser().getId();String key1 = "follows:"+userId;//2.求交集String key2= "follows:"+id;Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);if(intersect==null||intersect.isEmpty()){//无共同关注return Result.ok(Collections.emptyList());}//解析id集合List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());//查询用户List<UserDTO> users = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(users);}

关注推送

Feed流的模式

拉模式

(msg后面数据为时间戳)如下所示,赵六收件箱一般是空的,只有在赵六在读消息时才会来拉取他关注目标的发件箱,并按照时间排序

优点:节省内存空间,收件箱读完以后,就不用了可以清理,所以消息只保存一份在发件人的发件箱中

缺点:每次去读消息时,都要去拉取再做排序,耗时时间长,读取时间较

推模式

弥补拉模式的缺点,每次都会把消息发给每一个粉丝,并排序,等粉丝读取

缺点:这种扩散方式,每次更新消息时,都会发给每个粉丝,所以内存占用率很高

推拉结合模式

普通人采取推模式

大V,活跃粉丝采用推模式,普通的粉丝采用拉模式

总结

基于推模式实现关注推送功能

Feed流的分页问题

思考应该用Redis中哪种数据结构进行时间戳的排序?是sortedset还是list?

list查询数据只能按照角标查询,或者首尾查询,不支持滚动分页

sortedset按照score值排序,得出排名,如果按照排名查询则跟角标查询无区别,但是sortedset支持按照score值范围进行查询(把时间戳按照从大到小进行排列,每次排序都记住最小的时间戳,然后下次查询时再找比这个时间戳更小的,这样就实现了滚动分页查询)

小结:如果数据会有变化,尽量使用sortedset实现分页查询

采用传统角标查询会出现下列问题

使用滚动分页,每次存入页的最后一次角标

实现

Controller层

@PostMappingpublic Result saveBlog(@RequestBody Blog blog) {return blogService.saveBlog(blog);}

Service层

@Overridepublic Result saveBlog(Blog blog) {//获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());//2.保存笔记boolean isSuccess = save(blog);if(isSuccess == false){return Result.fail("新增笔记失败!");}//3.查询笔记作者所有粉丝//sql: select * from tb_follow where follow_user_id = ?List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();//4.推送笔记id给所有粉丝for (Follow follow : follows) {//4.1获取粉丝idLong userId = follow.getUserId();//4.2推送sortedSetString key = "feed:" + userId;stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());}//3.返回idreturn Result.ok(blog.getId());}

实现关注推送页面的分页查询

角标查询 和 通过记录最后一个的范围查询的对比

一开始redis中z1集合存入的数据("m6" : 6,"m5" : 5,"m4" : 4,"m3" : 3,"m2" : 2,"m1" : 1)

实现滚动分页查询一定要传入四个参数:

分数的最大值(max),分数的最小值(min),偏移量(offset),数量(count)(其中分数最小值,数量固定不变)

最大值每次都要找上一次查询的最小分数(除了第一次)

偏移量(第一次采取0,以后采取1,小于等于与小于的区别)

注意:如果分数一样会出现以下问题,所以offset应是上一次查询的最小分数的总个数

接口说明

Controller层

@GetMapping("/of/follow")public Result queryBlogOfFollow(@RequestParam("lastId") Long max,@RequestParam(value = "offset",defaultValue = "0") Integer offset){return blogService.queryBlogOfFollow(max,offset);}

Service层

@Overridepublic Result queryBlogOfFollow(Long max, Integer offset) {//1.获取当前用户Long userId = UserHolder.getUser().getId();//2.查询收件箱//ZREVRANGEBYSCORE key Max Min LIMIT offset countString key = "feed:" + userId;//元组Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);//非空判断if(typedTuples==null||typedTuples.isEmpty()){return Result.ok();}//3.解析数据:blogId、minTime(时间戳)、offsetList<Long> ids = new ArrayList<>(typedTuples.size());long minTime = 0;int os = 1;//偏移量for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {//3.1获取id(blogId)ids.add(Long.valueOf(typedTuple.getValue()));//3.2获取分数(时间戳)//执行到最一定是最小时间戳long time = typedTuple.getScore().longValue();if(time == minTime){os++;}else{minTime = time;os = 1;}}//4。根据id查询blog//不能采用下面方法查询 in 语句无序//List<Blog> blogs = listByIds(ids);//应使用String idStr = StrUtil.join(",", ids);List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();//5.封装并返回//需要将blog的信息完善for (Blog blog : blogs) {//查询blog相关用户queryBlogUser(blog);//查询blog是否被点赞isBlogLiked(blog);}ScrollResult scrollResult = new ScrollResult();scrollResult.setList(blogs);scrollResult.setOffset(offset);scrollResult.setMinTime(minTime);return Result.ok(scrollResult);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。