延迟任务精准发布文章

1)文章定时发布

2)延迟任务概述

2.1)什么是延迟任务

image-20210513145942962

应用场景:

场景一:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单;如果期间下单成功,任务取消

场景二:接口对接出现网络问题,1分钟后重试,如果失败,2分钟重试,直到出现阈值终止

2.2)技术对比

2.2.1)DelayQueue

JDK自带DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素,同时元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素

image-20210513150058814

DelayQueue属于排序队列,它的特殊之处在于队列的元素必须实现Delayed接口,该接口需要实现compareTo和getDelay方法

getDelay方法:获取元素在队列中的剩余时间,只有当剩余时间为0时元素才可以出队列。

compareTo方法:用于排序,确定元素出队列的顺序。

实现:

1:在测试包jdk下创建延迟任务元素对象DelayedTask,实现compareTo和getDelay方法,

2:在main方法中创建DelayQueue并向延迟队列中添加三个延迟任务,

3:循环的从延迟队列中拉取任务

DelayQueue实现完成之后思考一个问题:

使用线程池或者原生DelayQueue程序挂掉之后,任务都是放在内存,需要考虑未处理消息的丢失带来的影响,如何保证数据不丢失,需要持久化(磁盘)

2.2.2)RabbitMQ实现延迟任务

image-20210513150319742

2.2.3)redis实现

zset数据类型的去重有序(分数排序)特点进行延迟。例如:时间戳作为score进行排序

image-20210513150352211

3)redis实现延迟任务

实现思路

image-20210513150440342

问题思路

1.为什么任务需要存储在数据库中?

延迟任务是一个通用的服务,任何需要延迟得任务都可以调用该服务,需要考虑数据持久化的问题,存储数据库中是一种数据安全的考虑。

2.为什么redis中使用两种数据类型,list和zset?

效率问题,算法的时间复杂度

3.在添加zset数据的时候,为什么不需要预加载?

任务模块是一个通用的模块,项目中任何需要延迟队列的地方,都可以调用这个接口,要考虑到数据量的问题,如果数据量特别大,为了防止阻塞,只需要把未来几分钟要执行的数据存入缓存即可。

4)延迟任务服务实现

4.1)搭建heima-leadnews-schedule模块

leadnews-schedule是一个通用的服务,单独创建模块来管理任何类型的延迟任务

①:导入资料文件夹下的heima-leadnews-schedule模块到heima-leadnews-service下,如下图所示:

image-20210513151649297

②:添加bootstrap.yml

③:在nacos中添加对应配置,并添加数据库及mybatis-plus的配置

4.2)数据库准备

导入资料中leadnews_schedule数据库

 

taskinfo 任务表

image-20210513151812858

实体类

taskinfo_logs 任务日志表

image-20210513151835752

实体类

乐观锁支持:

 

4.3)安装redis

①拉取镜像

② 创建容器

③链接测试

打开资料中的Redis Desktop Manager,输入host、port、password链接测试

image-20210513152138388

能链接成功,即可

4.4)项目集成redis

① 在项目导入redis相关依赖,已经完成

② 在heima-leadnews-schedule中集成redis,添加以下nacos配置,链接上redis

③ 拷贝资料文件夹下的类:CacheService到heima-leadnews-common模块下,并添加自动配置

image-20210514181214681

 

④:测试

 

4.5)添加任务

①:拷贝mybatis-plus生成的文件,mapper

②:创建task类,用于接收添加任务的参数

③:创建TaskService

实现:

ScheduleConstants常量类

④:测试

 

4.6)取消任务

在TaskService中添加方法

实现

测试

4.7)消费任务

在TaskService中添加方法

实现

4.8)未来数据定时刷新

4.8.1)reids key值匹配

方案1:keys 模糊匹配

keys的模糊匹配功能很方便也很强大,但是在生产环境需要慎用!开发中使用keys的模糊匹配却发现redis的CPU使用率极高,所以公司的redis生产环境将keys命令禁用了!redis是单线程,会被堵塞

image-20210515162329679

方案2:scan

SCAN 命令是一个基于游标的迭代器,SCAN命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为SCAN命令的游标参数, 以此来延续之前的迭代过程。

image-20210515162419548

代码案例:

4.8.2)reids管道

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

image-20210515162537224

Pipeline请求模型

image-20210515162604410

官方测试结果数据对比

image-20210515162621928

测试案例对比:

 

4.8.3)未来数据定时刷新-功能完成

在TaskService中添加方法

在引导类中添加开启任务调度注解:@EnableScheduling

4.9)分布式锁解决集群下的方法抢占执行

4.9.1)问题描述

启动两台heima-leadnews-schedule服务,每台服务都会去执行refresh定时任务方法

image-20210516112243712

4.9.2)分布式锁

分布式锁:控制分布式系统有序的去对共享资源进行操作,通过互斥来保证数据的一致性。

解决方案:

image-20210516112457413

 

4.9.3)redis分布式锁

sexnx (SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

image-20210516112612399

这种加锁的思路是,如果 key 不存在则为 key 设置 value,如果 key 已存在则 SETNX 命令不做任何操作

4.9.4)在工具类CacheService中添加方法

修改未来数据定时刷新的方法,如下:

4.10)数据库同步到redis

image-20210721013255332

5)延迟队列解决精准时间发布文章

5.1)延迟队列服务提供对外接口

提供远程的feign接口,在heima-leadnews-feign-api编写类如下:

在heima-leadnews-schedule微服务下提供对应的实现

5.2)发布文章集成添加延迟队列接口

 

在创建WmNewsTaskService

实现:

 

枚举类:

序列化工具对比

拷贝资料中的两个类到heima-leadnews-utils下

Protostuff需要引导依赖:

 

修改发布文章代码:

把之前的异步调用修改为调用延迟任务

 

5.3)消费任务进行审核文章

WmNewsTaskService中添加方法

实现

在WemediaApplication自媒体的引导类中添加开启任务调度注解@EnableScheduling

6)作业