Redis实战案例24-关注推送

news2024/12/21 22:08:27

1. Feed流实现方案

在这里插入图片描述
在这里插入图片描述

拉模式主要缺点,延迟问题,极端情况某个用户关注了成千上万的up主,每位up主又发布了十几条博客,此时拉模式的延迟就会很高;

在这里插入图片描述

推模式缺点也很明显,内存消耗太大,假设up主是千万级别的,此时往每个粉丝的收件箱都要发送推文,收件箱要保存上亿份,消耗的内存太大;

在这里插入图片描述

对于普通的up主采用的是推模式,也就是直接发送给粉丝的收件箱,而自己没有发件箱;
而对于大V,有两种情况,如果是活跃度高的粉丝,采用的就是推模式,会直接获取信息,延时低;而对于那种普通不活跃或者僵尸粉,则采用的是拉模式,用户需要从发件箱中获取,延时会高一点;

在这里插入图片描述
一般达不到千万级别以上都是采用推模式,所以案例采用推模式,难度简单;

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

采用SortedSet数据结构
选择SortedSet原因:

  1. 对于变化的排名进行分页时采用SortedSet;
  2. 数据动态变化;
  3. 排行榜;

在这里插入图片描述

传统分页模式缺点:读取到重复数据

在这里插入图片描述

滚动分页实现:记录每一次查询的最后一条

在这里插入图片描述

/**
 * 保存博客,并且推送到粉丝的收件箱中
 * @param blog
 * @return
 */
@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.推送笔记给粉丝,所以首先需要获取当前用户的粉丝列表
    // user_id:用户id  follow_user_id:被关注的博主的id
    List<Follow> followUserIds = followService.query().eq("follow_user_id", user.getId()).list();
    // 4.遍历整个粉丝列表,推送给粉丝
    for (Follow follow:followUserIds) {
        // 4.1 获取粉丝id
        Long userId = follow.getUserId();
        // 4.2 推送笔记id给所有粉丝,key值为用户自身的id
        String key = "feed:" + userId;
        stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
    }
    //5. 返回id
    return Result.ok(blog.getId());
}

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

重点是如何解决滚动分页查询收件箱

在这里插入图片描述

如果按角标(value)查,角标变化会出现重复查询;

在这里插入图片描述

按照score值查,记录查询最小score;

在这里插入图片描述
测试时max取1000,实际业务时取当前时间为最大值;
限制为查询条数,offset表示偏移量,意思是上一条查询中,和最小值一样的条数,避免重复(一般取值为1,因为也不需要查询上一条查询中最小值);
count表示查询的条数;

在这里插入图片描述

在这里插入图片描述

偏移量继续设为0则会重复查询;

在这里插入图片描述

在这里插入图片描述
代码实现

第一次查询给默认值,时间戳为当前时间,偏移量为0
之后的值有后续代码给出;

返回值类:

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

实现类,见注释

/**
 * 滚动查询,展示博主推送的笔记,新发布的滚动查询查不到,但是往上滚,前端做了处理,就是刷新重新查询,开始位置在当前最新位置
 * @param max
 * @param offset
 * @return
 */
@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
    //1.找到用户
    UserDTO user = UserHolder.getUser();
    //2.查询用户收件箱
    String key = FEED_KEY + user.getId();
    Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
            .rangeByScoreWithScores(key, 0, max, offset, 2);
    //判空操作
    if(typedTuples == null || typedTuples.isEmpty()) {
        return Result.ok();
    }
    //3.解析收件箱数据:blogId、minTime(时间戳)、offset(偏移量)
    ArrayList<Object> ids = new ArrayList<>(typedTuples.size());
    long minTime = 0;  //这个minTime是上次查询的最小时间戳,作为当次查询的最大时间戳来开始查
    int os = 1; //计算分数值等于最小值的个数
    for (ZSetOperations.TypedTuple<String> typedTuple:typedTuples) {
        //获取博客id转换为Long型并存入ids数组中
        //获取id
        String id = typedTuple.getValue();
        ids.add(Long.valueOf(id));
        //获取分数(时间戳)
        long time = typedTuple.getScore().longValue();
        if(time == minTime){
            os += 1;
        }else {
            minTime = time;
            os = 1;
        }
    }
    //4.根据id查询blog,采用mp的批量查询
    //但是由于用mp提供的listByIds是用in方法查,不能保证顺序
    //需要加orderby语句,需要id字符串
    //使用last方法添加查询条件,按照给定的ID顺序进行排序。idStr是一个字符串,用于指定ID的顺序。
    String idStr = StrUtil.join(",", ids);
    List<Blog> blogList = query().in("id", ids).last("order by field(id," + idStr + ")").list();
    //其中每个blog都要做查询该blog博主信息和点赞信息的操作
	for (Blog blog:blogList) {
    	//查询blog有关的用户
    	queryBlogUser(blog);
    	//查询blog是否被点赞
    	isBlogLiked(blog);
	}
    //5.封装并返回
    ScrollResult r = new ScrollResult();
    r.setList(blogList);
    r.setOffset(os);
    r.setMinTime(minTime);
    return Result.ok(r);
}

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

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

相关文章

【idea工具】idea工具,build的时候提示:程序包 com.xxx.xx不存在的错误

idea工具&#xff0c;build的时候提示:程序包 com.xxx.xx不存在的错误&#xff0c;如下图&#xff0c;折腾了好一会&#xff0c; 做了如下操作还是不行&#xff0c;idea工具编译的时候&#xff0c;还是提示 程序包不存在。 a. idea中&#xff0c;重新导入项目&#xff0c;也还…

基于DNN深度学习网络的OFDM+QPSK信号检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................................. Transmitt…

区块链实验室(11) - PBFT耗时与流量特征

以前面仿真程序为例&#xff0c;分析PBFT的耗时与流量特征。实验如下&#xff0c;100个节点构成1个无标度网络&#xff0c;节点最小度为5&#xff0c;最大度为38. 从每个节点发起1次交易共识。统计每次交易的耗时以及流量。本文所述的流量见前述仿真程序的说明:区块链实验室(3)…

页面设计—FlexContainer弹性容器组件详解

一、组件介绍 Flex 是 Flexible Box 的缩写&#xff0c;意为"弹性布局"&#xff0c;用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为 Flex 布局&#xff0c;可以与栅格布局结合使用。 二、样式介绍 三、如何使用 &#xff08;1&#xff09;找到FlexCo…

实用!SD人物表情提示词合辑;秒变大神的Python速查表;开源LLM微调训练指南;LLM开发必修课 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 太实用了&#xff01;Stable Diffusion 的24个表情提示词 Pensive 沉思 Smiling 微笑 Disgusted 厌恶 Laughing 大笑 Shocked 震惊 Fr…

【数据分析专栏之Python篇】全网最细Anaconda安装与配置

文章目录 [toc] 前言一、 Anaconda是什么1.1 简介1.2 特点1.3 Anaconda、conda、pip、virtualenv的区别 二、为什么使用Anaconda三、安装步骤3.1 下载安装3.2 配置conda源 四、结语五、附录六、参考 前言 大家好&#xff01;本篇给大家介绍 Anaconda 安装及配置。 一、 Anaco…

【分享】揭秘BlueWillow::AI绘画工具的平替新选择

哈喽&#xff0c;木易巷上线啦&#xff01; 今天我要给大家介绍一个全新的AI绘画工具——BlueWillow。如果你正在寻找一款能够替代Midjourney的工具&#xff0c;那么BlueWillow绝对值得一试&#xff01; 官网链接和邀请链接都在最后哦&#xff01; 首先&#xff0c;BlueWillo…

< 今日小技巧:Axios封装,接口请求增加防抖功能 >

文章目录 &#x1f449; 前言&#x1f449; 一、核心代码 &#xff1a; 防抖函数&#x1f449; 二、Axios封装中的配置&#x1f449; 三、实现原理&#x1f449; 结论&#x1f449; 补充优化&#xff1a; 解决多个接口请求&#xff0c;拦截掉了需要的请求> 防抖函数> 引用…

c 学习笔记(自用)---GCC编译器

1.GCC编译器的使用 1&#xff09; 一个c文件预处理和编译的过程 可以用以上命令去逐步调试下&#xff0c;看看预处理的过程 2&#xff09;命令举例与解释 #源文件较少的情况下 gcc -o test main.c sub.c #分别将 main.c 和sub.c文件进行预处理、编译、汇编&#xff0c; #…

push_back 和 emplace_back 的区别

文章目录 1、vector::push_back1.1 void push_back(T&& x) ; (C11)参数返回值类型大小 和 容量移动左值用户自定义类型使用 push_back 1.2 void push_back(const T &x);参数返回值类型 1.3 如果 vector 的 size 超过当前capacity&#xff0c;push_back 会使迭代器、…

一分钟学一个 Linux 命令 - rm

前言 大家好&#xff0c;我是 god23bin&#xff0c;欢迎回到咱们的《一分钟学一个 Linux 命令》系列&#xff0c;今天我要讲的是一个比较危险的命令&#xff0c;rm 命令&#xff0c;没错&#xff0c;你可以没听过 rm 命令&#xff0c;但是删库跑路你不可能没听过吧&#xff1f…

RISCV -3 RV32I/RV64I基本整型指令集

RISCV -3 RV32I/RV64I基本整型指令集 1 RV32I Base Integer Instruction Set1.1 Programmers’ Model for Base Integer ISA1.2 Base Instruction Formats1.3 Immediate Encoding Variants1.4 Integer Computational Instructions1.4.1 Integer Register-Immediate Instruction…

25-30天每日强训选择题改错解析

int i5; int s(i)(i)(i–)(–i); s( )//s 的值是什么&#xff1f; A 28 B 25 C 21 D 26 E 24 F 23 正确答案&#xff1a; E 5775 24 或者 --在后先不变化数值 -- 在前先变化再运算 以下哪项不属于java类加载过程&#xff1f; A 生成java.lang.Class对象 B int类型对象成…

【Vue】day04-组件通信

day04 一、学习目标 1.组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09; scoped解决样式冲突/data是一个函数 2.组件通信 组件通信语法 父传子 子传父 非父子通信&#xff08;扩展&#xff09; 3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xf…

Windows下达梦数据库图形化安装、初始化数据库及连接

文章目录 前言一、达梦数据库安装1、下载安装包2、解压3、安装 二、初始化数据库三、连接数据库1、客户端工具2、输入连接信息3、成功连接 总结 前言 本节将介绍达梦数据库的图形化界面安装的细节&#xff0c;本节以Win11系统及DM8为基础进行讲解&#xff0c;虽然是图形化安装…

5. Bean 的作用域和生命周期

目录 1. Bean 被修改的案例 2. 作用域定义 2.1 Bean 的 6 种作用域 singleton prototype request session application&#xff08;了解&#xff09; websocket &#xff08;了解&#xff09; 单例作用域&#xff08;singleton&#xff09;VS 全局作用域&#xff08;…

企业知识管理系统安全是重中之重

企业开展知识管理工作的益处是全方位的&#xff0c;效果能从业务的各方面得到体现&#xff0c;最终效果就是企业竞争力的提升与企业经营业绩的提升。 知识管理系统的意义在于&#xff0c;构建系统的知识库&#xff0c;对纷杂的知识内容&#xff08;方案、策划、制度等&#xf…

【业务功能篇51】对象复制的三种方式 工具类Orika、反射、BeanUtils浅拷贝

业务场景&#xff1a; 设计规范前提下&#xff0c;我们数据层传输承载的是用DTO&#xff0c;而到控制层返回给前端会对应定义一个VO对象&#xff0c;比如是一个问题单数据集合list<DTO>,数据层接收的是DTO对对象&#xff0c;到控制层接收后需要转换成list<VO>,这里…

项目中如何使用文件IO?【大学学了好几门语言都有IO,到底怎么用?】

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class TestFile {public static void main(String[] args) throws IOException {// 通过这个简单的程序, 把一个文件的内容读取出…

Docker Compose 容器编排

Docker compose Docker compose 实现单机容器集群编排管理&#xff08;使用一个模板文件定义多个应用容器的启动参数和依赖关系&#xff0c;并使用docker compose来根据这个模板文件的配置来启动容器&#xff09; 通俗来说就是把之前的多条docker run启动容器命令 转换为docker…