头条项目的文章延迟发布功能

最近做的头条项目其中有个功能是创作者发表的文章可以设置在未来某个时间发表,在实现这个功能的时候就在想该怎么实现呢?刚开始想的是利用Spring的定时任务定时的去数据库中查询,可以这个查询频率该怎么设置,每次从数据库中需要查询文章延迟发布表的全部信息,未免有点太消耗时间了,况且MySQL还是存在本地磁盘的,读取成本过高。

这个时候我就想既然遇到了读写速度问题,就要找缓存来解决了,利用Redis存储在内存的特性,将马上就要发布的文章信息存进Redis中,利用定时任务一分钟查询一次缓存,查看是否有要发布的文章,拉取对应的文章信息进行审核发布

我在redis中使用两种数据结构来存储文章发布信息

list 是一个简单的字符串列表,按照插入顺序排序。你可以在列表的头部(左边)或尾部(右边)插入元素。

 使用list存储发布时间小于等于当前时间也就是立刻就要发布的文章,每次都从列表的左边插入文章信息,定时任务消费的时候从右边拉取数据,以形成一定的时间顺序

 zset 是一种特殊的集合,它和普通集合一样,成员都是唯一的,但每个成员都会关联一个分数(score),Redis 会根据分数对成员进行从小到大的排序。

zset和list的区别就是元素唯一,并且每个元素绑定一个分数, 该集合会根据分数的大小进行排序,正好就可以把文章的预发布时间当作score,这样每次取score最小的文章也就是最早要发布的文章,符合业务逻辑

延迟发布任务的存储处理

处理流程如上图所示,当有用户发起文章发布请求时

1 先将文章相关的信息存入本地数据库中做备份(防止因为系统或断电导致缓存丢失)

2 然后判断文章的预发布时间

        如果小于等于当前时间,直接放入list中等待定时任务消费

        否则如果发布时间在未来五分钟以内,放入zset中

    public long addTask(Task task) {//先存储进本地数据库boolean success=addTaskToDb(task);//存进缓存if(success){addTaskToCache(task);}return task.getTaskId();}/*** 把任务添加到redis中** @param task*/private void addTaskToCache(Task task) {String key = task.getTaskType() + "_" + task.getPriority();//获取5分钟之后的时间  毫秒值Calendar calendar = Calendar.getInstance();calendar.add(Calendar.MINUTE, 5);long nextScheduleTime = calendar.getTimeInMillis();//2.1 如果任务的执行时间小于等于当前时间,存入listif (task.getExecuteTime() <= System.currentTimeMillis()) {cacheService.lLeftPush(ScheduleConstants.TOPIC + key, JSON.toJSONString(task));} else if (task.getExecuteTime() <= nextScheduleTime) {//2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间(未来5分钟) 存入zset中cacheService.zAdd(ScheduleConstants.FUTURE + key, JSON.toJSONString(task), task.getExecuteTime());}}//MySQL数据private boolean addTaskToDb(Task task) {//用来标记是否存储成功boolean flag = false;try {//保存任务表Taskinfo taskinfo = new Taskinfo();BeanUtils.copyProperties(task, taskinfo);taskinfo.setExecuteTime(new Date(task.getExecuteTime()));taskinfoMapper.insert(taskinfo);//设置taskIDtask.setTaskId(taskinfo.getTaskId());//保存任务日志数据TaskinfoLogs taskinfoLogs = new TaskinfoLogs();BeanUtils.copyProperties(taskinfo, taskinfoLogs);taskinfoLogs.setVersion(1);taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);taskinfoLogsMapper.insert(taskinfoLogs);flag = true;} catch (Exception e) {e.printStackTrace();}return flag;}

以上仅仅只是请求到来后初步的处理,当消费列表list中的文章发布任务处理完毕后怎么办呢?

Redis数据处理

针对Redis中zset的数据(未来五分钟内发布的文章),需要每分钟查询是否有发布时间小于等于当前时间的,然后从zset中移动到list中

这就涉及到Redis列表的搜索算法了,目前常用的匹配对应元素的方法有keys 的模糊匹配、Scan扫描,由于keys模糊匹配非常占用CPU的时间,所以一般使用SCAN扫描符合要求的数据

当用户数据量较大是,如果从zset中一条一条的将文章发布任务移动到list中也很占用时间,恰好Redis提供了Pipeline请求服务,可以一次传送大量数据,大大节省时间

(同时,基于分布式的软件架构下可能有多个端同时处理文章预发布信息,这里使用redis的分布式锁,占用时间三十秒)

定时任务代码如下

    @Scheduled(cron = "0 */1 * * * ?")public void refresh() {//使用redis的分布式锁,三十秒后结束String token= cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);if(StringUtils.isNotBlank(token)){System.out.println(System.currentTimeMillis() / 1000 + "执行了定时任务");// 获取所有未来数据集合的key值,使用scan而非keysSet<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");// future_*for (String futureKey : futureKeys) { // future_250_250String topicKey = ScheduleConstants.TOPIC + futureKey.split(ScheduleConstants.FUTURE)[1];//获取该组key下当前需要消费的任务数据Set<String> tasks = cacheService.zRangeByScore(futureKey, 0, System.currentTimeMillis());if (!tasks.isEmpty()) {//将这些任务数据添加到消费者队列中cacheService.refreshWithPipeline(futureKey, topicKey, tasks);System.out.println("成功的将" + futureKey + "下的当前需要执行的任务数据刷新到" + topicKey + "下");}}}}

普通redis客户端和服务器交互模式

Pipeline请求模型

官方测试结果数据对比

延迟发布任务的消费

上面已经解决了文章预发布任务的处理,下面就是从缓存中定时的拉取任务进行文章发布了

在自媒体段使用Feign接口远程调用任务模块的poll方法拉取缓存中的任务

    @Scheduled(fixedRate = 1000)@SneakyThrows@Overridepublic void scanNewsByTask() {log.info("文章审核---消费任务执行---begin---");//从缓存中拉取文章发布任务ResponseResult responseResult = scheduleClient.poll(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType(),TaskTypeEnum.NEWS_SCAN_TIME.getPriority());if(responseResult.getCode().equals(200) && responseResult.getData() != null){String json_str = JSON.toJSONString(responseResult.getData());Task task = JSON.parseObject(json_str, Task.class);byte[] parameters = task.getParameters();WmNews wmNews = ProtostuffUtil.deserialize(parameters, WmNews.class);//审核文章内容wmNewsAutoScanService.autoScanWmNews(wmNews.getId());}log.info("文章审核---消费任务执行---end---");}

 任务模块的poll

    public Task poll(int type,int priority) {Task task = null;try {String key = type+"_"+priority;String task_json = cacheService.lRightPop(ScheduleConstants.TOPIC + key);if(StringUtils.isNotBlank(task_json)){task = JSON.parseObject(task_json, Task.class);//更新数据库信息updateDb(task.getTaskId(),ScheduleConstants.EXECUTED);}}catch (Exception e){e.printStackTrace();log.error("poll task exception");}return task;}

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

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

相关文章

Celery 全面指南:Python 分布式任务队列详解

Celery 全面指南&#xff1a;Python 分布式任务队列详解 Celery 是一个强大的分布式任务队列/异步任务队列系统&#xff0c;基于分布式消息传递&#xff0c;专注于实时处理&#xff0c;同时也支持任务调度。本文将全面介绍 Celery 的核心功能、应用场景&#xff0c;并通过丰富…

OpenHarmony NativeC++应用开发speexdsp噪声消除案例

随着5.0的版本的迭代升级&#xff0c;笔者感受到了开源鸿蒙前所未有大的版本更替速度。5.0出现了越来越多的C API可以调用&#xff0c;极大的方便了native c应用的开发。笔者先将speexdsp噪声消除的案例分享&#xff0c;老规矩&#xff0c;还是开源&#xff01;&#xff01;&am…

nuxt3 seo优化

在 Nuxt3 中&#xff0c;通过 nuxtjs/seo、nuxtjs/sitemap 和 nuxtjs/robots 模块可以生成包含动态链接的站点地图&#xff08;sitemap.xml&#xff09;&#xff0c;但具体是“实时生成”还是“部署时生成”&#xff0c;取决于你的配置方式和数据更新频率。以下是具体分析&…

es6的100个问题

基础概念 解释 let、const 和 var 的区别。什么是块级作用域&#xff1f;ES6 如何实现它&#xff1f;箭头函数和普通函数的主要区别是什么&#xff1f;解释模板字符串&#xff08;Template Literals&#xff09;的用途&#xff0c;并举例嵌套变量的写法。解构赋值的语法是什么…

【机器学习】什么是决策树?

什么是决策树&#xff1f; 决策树是一种用于分类和回归问题的模型。它通过一系列的“决策”将数据逐步分裂&#xff0c;最终得出预测结果。可以把它看作是一个“树”&#xff0c;每个节点表示一个特征的判断&#xff0c;而每个分支代表了可能的判断结果&#xff0c;最终的叶子…

Java面试黄金宝典15

1. 请找出增序排列中一个数字第一次和最后一次出现的数组下标 定义 由于数组是增序排列的&#xff0c;我们可以利用二分查找的特性来高效地定位目标数字。对于查找第一次出现的位置&#xff0c;当中间元素等于目标数字时&#xff0c;我们需要继续向左搜索&#xff0c;以确保找…

CentOS 7安装 mysql

CentOS 7安装 mysql 1. yum 安装 mysql 配置mysql源 yum -y install mysql57-community-release-el7-10.noarch.rpm安装MySQL服务器 yum -y install mysql-community-server启动MySQL systemctl start mysqld.service查看MySQL运行状态&#xff0c;运行状态如图&#xff…

科软25机试

题目: 2025科软复试上机题&#xff08;回忆版&#xff09;题解_哔哩哔哩_bilibili 1. 字符串反转 #include<bits/stdc.h> using namespace std;void solve(string& a, int CurN) {if (!(CurN % 2)) {int right a.size() - 1;int left 0;while (left < right)…

Oracle相关的面试题

以下是150道Oracle相关的面试题&#xff0c;涵盖了Oracle的基础概念、架构、SQL与PL/SQL、性能调优、高可用性、备份与恢复、安全、分区与索引、存储与内存管理、网络与连接、版本与升级等方面&#xff0c;希望对你有所帮助。 Oracle基础概念 1. 什么是Oracle数据库&#xff1…

docker安装,镜像,常用命令,Docker容器卷,Docker应用部署,自定义镜像,Docker服务编排,创建私有仓库

1.为什么使用docker 如果开发环境和测试环境的允许软件版本不一致&#xff0c;可能会导致项目无法正常启动 把环境和项目一起打包发送给测试环境 1.1docker的概念 开源的应用容器引擎&#xff0c;完全使用沙箱机制&#xff0c;相互隔离&#xff0c;容器性能开销极低 一种容…

ES 字段的映射定义了字段的类型及其行为

在 Elasticsearch 中&#xff0c;字段的映射定义了字段的类型及其行为。你提供的 content_answer 字段映射如下&#xff1a; Json 深色版本 "content_answer": { "type": "text", "fields": { "keyword": { …

Manus的开源替代者之一:OpenManus通用AI智能体框架解析及产品试用

引言 在AI智能体领域&#xff0c;Monica团队近期发布的Manus被誉为全球首个通用型AI智能体。该项目推出后迅速爆红&#xff0c;邀请码一号难求&#xff0c;随之而来的是各路开发者快速构建了众多类似的开源替代方案。其中&#xff0c;MetaGPT团队的5位工程师仅用3小时就开发完…

Linux MariaDB部署

1&#xff1a;查看Linux系统版本 cat /etc/os-release#返回结果&#xff1a; NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_ID"7" PRETTY_NAME"CentOS Linux 7 (Core)" ANSI…

PHP MySQL 预处理语句

PHP MySQL 预处理语句 引言 在PHP中与MySQL数据库进行交互时,预处理语句是一种非常安全和高效的方法。预处理语句不仅可以防止SQL注入攻击,还可以提高数据库查询的效率。本文将详细介绍PHP中预处理语句的用法,包括其基本概念、语法、优势以及在实际开发中的应用。 预处理…

算法 | 2024最新算法:鳑鲏鱼优化算法原理,公式,应用,算法改进研究综述,matlab代码

2024最新鳑鲏鱼优化算法(BFO)研究综述 鳑鲏鱼优化算法(Bitterling Fish Optimization, BFO)是2024年提出的一种新型群智能优化算法,受鳑鲏鱼独特的繁殖行为启发,通过模拟其交配、产卵和竞争机制进行全局优化。该算法在多个领域展现出优越性能,尤其在解决复杂非线性问题中…

HDR(HDR10/ HLG),SDR

以下是HDR&#xff08;HDR10/HLG&#xff09;和SDR的详细解释&#xff1a; 1. SDR&#xff08;Standard Dynamic Range&#xff0c;标准动态范围&#xff09; • 定义&#xff1a;SDR是传统的动态范围标准&#xff0c;主要用于8位色深的视频显示&#xff0c;动态范围较窄&…

uni-app页面怎么设计更美观

顶部 页面最顶部要获取到手机设备状态栏的高度&#xff0c;避免与状态栏重叠或者被状态栏挡住 // 这是最顶部的父级容器 <view :style"{ paddingTop: ${statusBarHeight extraPadding}px }">.... </view> export default {data() {return {statusBarH…

江西核威环保科技:打造世界前沿的固液分离设备高新企业

随着市场经济的不断发展&#xff0c;消费者的需求越来越大&#xff0c;为了更好的服务广大新老客户&#xff0c;作为知名品牌的“江西核威环保科技有限公司&#xff08;以下简称江西核威环保科技&#xff09;”&#xff0c;将坚持以“服务为企业宗旨&#xff0c;全力打造世界前…

Ethernet(以太网)详解

一、Ethernet的定义与核心特性 以太网&#xff08;Ethernet&#xff09;是一种 基于IEEE 802.3标准的局域网&#xff08;LAN&#xff09;技术&#xff0c;用于设备间通过有线或光纤介质进行数据通信。其核心特性包括&#xff1a; 标准化&#xff1a;遵循IEEE 802.3系列协议&am…

JBDev - Theos下一代越狱开发工具

JBDev - Theos下一代越狱开发工具 自越狱诞生以来&#xff0c;Theos一直是越狱开发的主流工具&#xff0c;大多数开发者使用Theos编译代码&#xff0c;再用lldb手动调试。JBDev简化了这个过程&#xff0c;项目地址https://github.com/lich4/JBDev 简介 JBDev用于Xcode越狱开…