我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Redis实现好友关注的示例代码

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Redis实现好友关注的示例代码

一、关注和取关

加载的时候会先发请求看是否关注了,来显示是关注按钮还是取关按钮

当我们点击关注或取关之后再发请求进行操作

Redis实现好友关注的示例代码

数据库表结构

关注表(主键、用户id、关注用户id)

需求

  • 关注和取关接口
  • 判断是否关注接口

@PutMapping("/{id}/{isFollow}")
public Result follow(@PathVariable("id") Long id, @PathVariable("isFollow") Boolean isFollow){
    return followService.follow(id,isFollow);
}
 

@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable("id") Long id){
    return followService.isFollow(id);
}

@Override
public Result follow(Long id, Boolean isFollow) {
    //获取当前用户id
    Long userId = UserHolder.getUser().getId();
    //判断是关注操作还是取关操作
    if(BooleanUtil.isTrue(isFollow)){
        //关注操作
        Follow follow = new Follow();
        follow.setUserId(userId);
        follow.setFollowUserId(id);
        save(follow);
    }else{
        //取关操作
        remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",id));
    }
    return Result.ok();
}
 

@Override
public Result isFollow(Long id) {
    //获取当前用户id
    Long userId = UserHolder.getUser().getId();
    Integer count = query().eq("user_id", userId).eq("follow_user_id", id).count();
    if(count>0){
        return Result.ok(true);
    }
    return Result.ok(false);
}

二、共同关注                               

需求:利用Redis中恰当的数据结构,实现共同关注功能,在博主个人页面展示当前用户和博主的共同好友

可以用redis中set结构的取交集实现

 先在关注和取关增加存入redis


@Override
public Result follow(Long id, Boolean isFollow) {
    //获取当前用户id
    Long userId = UserHolder.getUser().getId();
    String key = "follow:" + userId;
    //判断是关注操作还是取关操作
    if(BooleanUtil.isTrue(isFollow)){
        //关注操作
        Follow follow = new Follow();
        follow.setUserId(userId);
        follow.setFollowUserId(id);
        boolean success = save(follow);
        if(success){
            //插入set集合中
            stringRedisTemplate.opsForSet().add(key,id.toString());
        }
    }else{
        //取关操作
        boolean success = remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_id", id));
        //从set集合中移除
        if(success){
            stringRedisTemplate.opsForSet().remove(key,id.toString());
        }
    }
    return Result.ok();
}

然后就可以开始写查看共同好友接口了


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

@Override
public Result followCommons(Long id) {
    Long userId = UserHolder.getUser().getId();
    //当前用户的key
    String key1 = "follow:" + userId;
    //指定用户的key
    String key2 = "follow:" + id;
    //判断两个用户的交集
    Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);
    if(intersect==null||intersect.isEmpty()){
        //说明没有共同关注
        return Result.ok();
    }
    //如果有共同关注,则获取这些用户的信息
    List<Long> userIds = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
    List<UserDTO> userDTOS = userService.listByIds(userIds).stream().map(item -> (BeanUtil.copyProperties(item, UserDTO.class))).collect(Collectors.toList());
    return Result.ok(userDTOS);
}

三、关注推送(feed流)

关注推送也叫做fedd流,直译为投喂。为用户持续的提供"沉浸式"的体验,通过无限下拉刷新获取新的信息。feed模式,内容匹配用户。

Feed流产品有两种常见模式:

Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈

  • 优点:信息全面,不会有缺失。并且实现也相对简单
  • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低

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

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

本例中是基于关注的好友来做Feed流的,因此采用Timeline的模式。

1、Timeline模式的方案

该模式的实现方案有

  • 拉模式
  • 推模式
  • 推拉结合 

 拉模式

Redis实现好友关注的示例代码

优点:节省内存消息,只用保存一份,保存发件人的发件箱,要读的时候去拉取就行了

缺点:每次读取都要去拉,耗时比较久

推模式

Redis实现好友关注的示例代码

优点:延迟低

缺点:太占空间了,一个消息要保存好多遍

推拉结合模式

推拉结合分用户,比如大v很多粉丝就采用推模式,有自己的发件箱,让用户上线之后去拉取。普通人发的话就用推模式推给每个用户,因为粉丝数也不多直接推给每个人延迟低。粉丝也分活跃粉丝和普通粉丝,活跃粉丝用推模式有主机的收件箱,因为他天天都看必看,而普通粉丝用拉模式,主动上线再拉取,僵尸粉直接不会拉取,就节省空间。

Redis实现好友关注的示例代码

总结

Redis实现好友关注的示例代码

 由于我们这点评网站,用户量比较小,所以我们采用推模式(千万以下没问题)。

2、推模式实现关注推送

需求

(1)修改新增探店笔记的业务,在保存blog到数据库的同时,推送到粉丝的收件箱

(2)收件箱满足可以根据时间排序,必须用redis的数据结构实现

(3)查询收件箱数据时,可以实现分页查询

要进行分页查询,那么我们存入redis采用什么数据类型呢,是list还是zset呢

feed流分页问题

假如我们在分页查询的时候,这个时候加了新的内容11, 再查询下一页的时候,6就重复出现了,为了解决这种问题,我们必须使用滚动分页

Redis实现好友关注的示例代码

feed流的滚动分页

滚动分页就是每次都记住最后一个id,方便下一次进行查询,用这种lastid的方式来记住,不依赖于角标,所以我们不会收到角标的影响。所以我们不能用list来存数据,因为他依赖于角标,zset可以根据分数值范围查询。我们按时间排序,每次都记住上次最小的,然后从比这小的开始。

Redis实现好友关注的示例代码

实现推送到粉丝的收件箱

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

@Override
public Result saveBlog(Blog blog) {
    // 1.获取登录用户
    UserDTO user = UserHolder.getUser();
    blog.setUserId(user.getId());
    // 2.保存探店笔记
    boolean isSuccess = save(blog);
    if(!isSuccess){
        return Result.fail("新增笔记失败!");
    }
    // 3.查询笔记作者的所有粉丝 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.获取粉丝id
        Long userId = follow.getUserId();
        // 4.2.推送
        String key = FEED_KEY + userId;
        stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
    }
    // 5.返回id
    return Result.ok(blog.getId());
}

滚动分页接收思路

第一次查询是分数(时间)从1000(很大的数)开始到0(最小)这个范围,然后限制查3个(一页数量),偏移量是0,然后记录结尾(上一次的最小值)

以后每次都是从上一次的最小值到0,限定查3个,偏移量是1(因为记录的那个值不算),再记录结尾的值。

但是有一种情况,如果有相同的时间,分数一样的话,比如两个6分,而且上一页都显示完,我们下一页是按照第一个6分当结尾的,第二个6分可能会出现的,所以我们这个偏移量不能固定是1,要看有几个和结尾相同的数,如果是两个就得是2,3个就是3。

滚动分页查询参数:

  • 最大值:当前时间戳 | 上一次查询的最小时间戳
  • 最小值:0
  • 偏移量:0 | 最后一个值的重复数
  • 限制数:一页显示的数

Redis实现好友关注的示例代码

实现滚动分页查询

前端需要传来两条数据,分别是lastId和offset,如果是第一次查询,那么这两个值是固定的,会由前端来指定,lastId是发起查询时的时间戳,而offset就是零,当后端查询完分页信息后需要返回三条数据,第一条自然就是分页信息,第二条是此次分页查询数据中最后一条数据的时间戳,第三条信息是偏移量,我们需要在分页查询后计算有多少条信息的时间戳与最后一条是相同的,作为偏移量来返回。而前端拿到这后两个参数之后就会分别保存在前端的lastId和offset中,下一次分页查询时就会将这两条数据作为请求参数来访问,然后不断循环上述过程,这样也就实现了分页查询。

定义返回值实体类

@Data
public class ScrollResult {
    private List<?> list;
    private Long minTime;
    private Integer offset;
}

Controller

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

BlogServiceImpl

@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
    //获取当前用户
    Long userId = UserHolder.getUser().getId();
    //组装key
    String key = RedisConstants.FEED_KEY + userId;
    //分页查询收件箱,一次查询两条 ZREVRANGEBYSCORE key Max Min LIMIT offset count
    Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);
    //若收件箱为空则直接返回
    if (typedTuples == null || typedTuples.isEmpty()) {
        return Result.ok();
    }
    //通过上述数据获取笔记id,偏移量和最小时间
    ArrayList<Long> ids = new ArrayList<>();
    long minTime = 0;
    //因为这里的偏移量是下一次要传给前端的偏移量,所以初始值定为1
    int os = 1;
    for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
        //添加博客id
        ids.add(Long.valueOf(typedTuple.getValue()));
        //获取时间戳
        long score = typedTuple.getScore().longValue();
        //由于数据是按时间戳倒序排列的,因此最后被赋值的就是最小时间
        if (minTime == score) {
            //如果有两个数据时间戳相等,那么偏移量开始计数
            os++;
        } else {
            //如果当前数据的时间戳与已经记录的最小时间戳不相等,则说明当前时间小于已记录的最小时间戳,将其赋给minTime
            minTime = score;
            //偏移量重置
            os = 1;
        }
    }
    //需要考虑到时间戳相等的消息数量大于2的情况,这时候偏移量就需要加上上一页查询时的偏移量
    os = minTime == max ? os : os + offset;
 
    //根据id查询blog
    String idStr = StrUtil.join(",", ids);
    //查询时需要手动指定顺序
    List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();
    //这里还需要查询博客作者的相关信息,这里对比视频中,用一次查询代替了多次查询,提高效率
    List<Long> blogUserIds = blogs.stream().map(blog -> blog.getUserId()).collect(Collectors.toList());
    String blogUserIdStr = StrUtil.join(",", blogUserIds);
    HashMap<Long, User> userHashMap = new HashMap<>();
    userService.query().in("id", blogUserIds).last("ORDER BY FIELD(id," + blogUserIdStr + ")").list().
        stream().forEach(user -> {
        userHashMap.put(user.getId(), user);
    });
    //为blog封装数据
    Iterator<Blog> blogIterator = blogs.iterator();
    while (blogIterator.hasNext()) {
        Blog blog = blogIterator.next();
        User user = userHashMap.get(blog.getUserId());
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
        blog.setIsLike(isLikeBlog(blog.getId()));
    }
    //返回封装数据
    ScrollResult scrollResult = new ScrollResult();
    scrollResult.setList(blogs);
    scrollResult.setMinTime(minTime);
    scrollResult.setOffset(os);
    return Result.ok(scrollResult);
}

到此这篇关于Redis实现好友关注的示例代码的文章就介绍到这了,更多相关Redis 好友关注内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Redis实现好友关注的示例代码

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Redis实现好友关注的示例代码

目录一、关注和取关二、共同关注 &编程nbsp; 三、关注推送(feed流)1、Timeline模式的方案2、推模式实现关注推送一、关注和取关加载的时候会先发请求看是否关注了,来显示
2023-01-29

Redis实现UV统计的示例代码

目录一、HyperlogLog1、为什么用HyperLogLog2、HyperLogLog是什么二、实现UV统计一、HyperLogLog1、为什么用HyperLogLog先介绍两个概念:UV:全称 Unique Visitor,也叫
2023-01-29

Redis实现接口防抖的示例代码

目录实现演示说明:实际开发中,我们在前端页面上点击了一个按钮,访问了一个接口,这时因为网络波动或者其他原因,页面上没有反应,用户可能会在短时间内再次点击一次或者用户以为没有点到,很快的又点了一次。导致短时间内发送了两个请求到后台,可能会导致
Redis实现接口防抖的示例代码
2024-10-14

Redis实现商品秒杀的示例代码

本文详细讲解了使用Redis实现商品秒杀的示例代码。通过代码示例,文章展示了如何使用Redis的原子性操作和高性能来处理高并发秒杀场景。代码使用Redis的decr命令来原子性地扣除库存,确保秒杀过程的数据一致性。此外,Redis的可扩展性和分布式特性也使其能够满足大规模秒杀的需求。
Redis实现商品秒杀的示例代码
2024-04-02

Redis实现用户签到的示例代码

目录一、BitMap功能演示二、实现签到功能三、签到统计一、BitMap功能演示我们针对签到功能完全可以通过mysql来完成,比如说以下这张表用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数
Redis实现用户签到的示例代码
2024-09-27

nginx+lua+redis实现降级的示例代码

目录前言一、引子二、什么是降级, 为什么降级,降级的场景?三、降级的种类四、 业务分析五、 设计分析六、 降级的线路图七、实现降级八、验证降级九、自动降级前言商城或web站点的用户访问量出乎意料地增加了很多,超出了系统的负载能力, 系统有
nginx+lua+redis实现降级的示例代码
2024-10-10

用Python登录好友QQ空间点赞的示例代码

记得之前跟我女票说过,说要帮她空间点赞,点到999就不点了。刚开始还能天天记得,但是后来事情一多,就难免会忘记,前两天点赞的时候忽然觉得这样好枯燥啊,正好也在学Python,就在想能不能有什么方法能自动点赞。以前学C借助win32API也干
2022-06-04

SpringBoot实现redis延迟队列的示例代码

本篇文章介绍了SpringBoot实现Redis延迟队列的示例代码,采用了zset有序集合和list类型。入队任务时设置时间戳score,时间戳到达时任务从zset弹出执行,同时从list中移除。定时任务定时执行任务和移除过期任务,保证队列的正常运作。需要注意redisTemplate的连接池配置、定时任务执行间隔、过期任务清理策略等细节。
SpringBoot实现redis延迟队列的示例代码
2024-04-02

Redis实现短信验证码登录的示例代码

目录效果图pom.xmlapplicatoin.ymlRedis配置类controllerserviceImplmapper效果图发送验证码输入手机号、密码以及验证码完成登录操作pom.xml核心依赖
2022-06-13

python实现布尔型盲注的示例代码

好久没写python了,就想着写个简单的练练手,写个布尔型盲注自动化脚本,我觉得这个功能写的非常全了,这里是参考sqli-labs里面的盲注漏洞进行的脚本编写。 脚本运行时间:6分半左右bool_sqlblind.py # -*- codi
2022-06-02

WPF实现好看的Loading动画的示例代码

这篇文章主要介绍了如何利用WPF实现好看的Loading动画效果,文中的示例代码讲解详细,对我们学习或工作有一定帮助,需要的可以参考一下
2022-11-13

Redis shake实现可视化监控的示例代码

使用RedisShake可视化监控示例代码,可轻松实现RedisShake的运行状况和性能监控。该代码涵盖了RedisShakePrometheusExporter的安装、配置和使用,以及在Prometheus和Grafana中设置RedisShake目标、仪表盘和关键监控指标。通过可视化监控,可以实时了解RedisShake的运行状况,及时识别问题并采取纠正措施,从而提高其可用性和可靠性。
Redis shake实现可视化监控的示例代码
2024-04-02

编程热搜

目录