Redis实战案例24-关注推送

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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全 Day20-计算机网络基础知识05(网络原理)

计算机网络基础知识05&#xff08;网络原理&#xff09; 1. OSI 模型2. VMware虚拟机NAT模式下上网原理3. 不能上网故障排查 1. OSI 模型 OSI 7层网络通信原理模型 OSI 国际网互联 OSI 数据包封装解封装过程 北京局域网主机A到深圳局域网主机B数据工作流程 2. VMware虚拟机N…

《零基础入门学习Python》第066讲:GUI的终极选择:Tkinter3

今天我们一起来学习三个组件&#xff1a;Checkbutton、Radiobutton 和 LabelFrame。 同样&#xff0c;我们还是通过案例来讲解。 一、Checkbutton Checkbutton&#xff08;多选框&#xff09;。大家都知道什么叫做“翻牌子”&#xff0c;今天的第一个例子就是来写一个翻牌子…

巅峰极客2023 hellosql

随便输一个payload&#xff0c;有waf 这题只有两个回显&#xff0c;分别是太酷啦和nonono&#xff0c;不显示报错、登录成功等各种信息&#xff0c;目前只能想到用时间盲注。 抓包fuzz&#xff0c;194都是被过滤的 不止这些&#xff0c;手工测出来if、sleep、benchmark、*、rp…

opencv-20 深入理解HSV 色彩空间(通过指定,标记颜色等来拓展ROI区域)

RGB 色彩空间是一种被广泛接受的色彩空间&#xff0c;但是该色彩空间过于抽象&#xff0c;我们不能够直接通过其值感知具体的色彩。 我们更习惯使用直观的方式来感知颜色&#xff0c;HSV 色彩空间提供了这样 的方式。 通过 HSV色彩空间&#xff0c;我们能够更加方便地通过色调、…

Keil系列教程08_Configuration(二)

1写在前面 本文接着上一篇文章《Keil系列教程07_Configuration&#xff08;一&#xff09;》讲述的工程目标选项的后三项配置&#xff1a;Shortcut Keys快捷键、Text Completion代码完形、Other其他。 这后面三部分内容在该系列教程其它也会牵涉&#xff0c;也是一些常用、重要…

idea中常用的快捷键

快捷键: 1.快速生成main方法 main/psvm public static void main(String[] args) {} 2.快速复制当前行的代码: ctrld 3.快速捕获异常: altctrlt 4.快速打印结果: sout/soutv System.out.println(s); System.out.println("s " s); 5.自动生成对象的返回值…

tinkerCAD案例:8.Cylinder Earrings 圆筒耳环

tinkerCAD案例&#xff1a;8.Cylinder Earrings 圆筒耳环 In this lesson you will learn how to create earrings using cylinder shapes. So let’s get started! 在本课中&#xff0c;您将学习如何使用圆柱形制作耳环。所以让我们开始吧&#xff01; 说明 Drag a Cylinder …

el-Cascader 中div上绑定keyDown事件

keydown&#xff0c;keyup&#xff0c;keypress 事件默认是给页面上可以聚焦的元素绑定键盘事件&#xff0c;例如input输入框&#xff0c;点击输入框即代表聚焦在该元素上。那么想要给div或者其他不能聚焦的元素上使用键盘事件怎么处理呢&#xff1f;这里用到tabindex属性。 …

SWF格式视频怎么转换成AVI格式?简单的转换方法分享

当你想要在不同的设备上播放视频时&#xff0c;将SWF格式视频转换成AVI格式是非常有用的。因为SWF格式通常只能在特定的软件或网页上播放&#xff0c;而AVI格式则可以在更广泛的设备上播放&#xff0c;包括智能手机&#xff0c;平板电脑和电视机等。那么我们怎么将SWF转换成AVI…

关于Nginx的 location 配置各种情况转换后的样子记录

Nginx location 配置要代理的地址测试URL代理后的URL举例编号/test01http://127.0.0.1:8080/test01/abc/test/test01/abc/test01/test02http://127.0.0.1:8080//test02/abc/test//abc/test02/test03/http://127.0.0.1:8080/test03/abc/test/test03/abc/test03/test04/http://12…

Drools用户手册翻译——第四章 Drools规则引擎(六)执行控制

主要讲了规则匹配时的优先级设置的方式。 甩锅声明&#xff1a;本人英语一般&#xff0c;翻译只是为了做个笔记&#xff0c;所以有翻译错误的地方&#xff0c;错就错了&#xff0c;如果你想给我纠正&#xff0c;就给我留言&#xff0c;我会改过来&#xff0c;如果懒得理我&…

css终极方案PostCSS

一见如故 原理 所有的css框架都在一样的事&#xff0c;那就是由一个css生成一个新的css&#xff0c;那么postcss就来做了一个抽离&#xff1a; 1、将原有的css解析成抽象语法树 2、中间经过若干个插件 3、重新文本化&#xff0c;形成新的css postcss.config.js module.expor…

Spring Cloud 负载平衡的意义什么?

目录 一、什么是负载平衡 二、为什么需要负载平衡 三、Spring Cloud 如何实现负载平衡 四、负载平衡的nginx配置 一、什么是负载平衡 负载平衡是指将网络流量在多个服务器之间分布&#xff0c;以达到提高系统性能、增强可靠性和提供更好用户体验的目的。在负载平衡的架构中…

32位Cortex-M4 MCU:LPC54607J256ET180E、LPC54605J512BD100K 180MHz嵌入式微控制器

LPC546xx 32 位微控制器(MCU) 具有丰富的外设集、极低的功耗和增强的调试功能。 LPC546xx MCU系列采用ARM Cortex-M4内核&#xff0c;可提供以太网支持&#xff0c;并设有一个TFT LCD控制器和两个CAN FD模块。LPC546xx MCU旨在提高灵活性和性能可扩展性&#xff0c;可提供高达1…

Java - 泛型

泛型 1、 当我们ArrayList表示存放到ArrayList集合中的元素是Dog类型时 2、如果编译器发现添加的类型&#xff0c;不满足要求&#xff0c;就会报错。 3、遍历的时候直接取出Dog类型&#xff0c;而不是Object。 ArrayList<Week> weeks1 new ArrayList<>();好处…

科研院所用泛微搭建信创办公平台,统一办公,业务融合,安全便捷

国家全面推动重要领域的信创改造工作&#xff0c;要求到2027年底&#xff0c;对综合办公、经营管理、生产运营等系统实现“应替尽替、能替则替”。 科研机构作为智力、知识密集型机构&#xff0c;承载着大量数据、信息资产&#xff0c;数字化程度高&#xff0c;业务系统多样&a…

SpringBoot项目修改Tomcat版本号

SpringBoot项目修改Tomcat版本号 前言如果项目是以jar包形式打包部署如果项目是以war包形式打包部署示例 仰天大笑出门去&#xff0c;我辈岂是蓬蒿人 前言 Springboot项目,默认是使用内嵌Tomcat servlet容器形式打包部署。关于怎么修改默认的版本号&#xff0c;捣鼓了好久终于…

NLP(六十)Baichuan-13B-Chat模型使用体验

2023年7月11日&#xff0c;百川智能正式发布参数量130亿的通用大语言模型Baichuan-13B-Base、对话模型Baichuan-13B-Chat及其INT4/INT8两个量化版本。   本文将介绍大模型BaiChuan-13B-Chat的使用体验&#xff0c;其HuggingFace网址为&#xff1a;https://huggingface.co/bai…

8. Vmvare中重新分配Linux系统的分区空间大小

1. 说明 一般情况下&#xff0c;在使用Vmvare虚拟机创建配置Linux系统时&#xff0c;默认将系统的内存设置为4GB&#xff0c;硬盘大小设置为40GB&#xff0c;但随着空间利用的越来越多&#xff0c;内存会出现不够使用的情况&#xff0c;此时需要重新分配空间大小&#xff0c;具…

一起学数据结构(1)——复杂度

目录 1. 时间复杂度&#xff1a; 1.1 时间复杂度的概念&#xff1a; 1.2 时间复杂度的表示及计算&#xff1a; 1.3 较为复杂的时间复杂度的计算&#xff1a; 2. 空间复杂度&#xff1a; 2.1 空间复杂度的概念&#xff1a; 2.2 空间复杂度的计算&#xff1a; 1. 时间复杂度…