什么是延时任务
延时任务,顾名思义,就是延迟一段时间后才执行的任务。举个例子,假设我们有个发布资讯的功能,运营需要在每天早上7点准时发布资讯,但是早上7点大家都还没上班,这个时候就可以使用延时任务来实现资讯的延时发布了。只要在前一天下班前指定第二天要发送资讯的时间,到了第二天指定的时间点资讯就能准时发出去了。如果大家有运营过公众号,就会知道公众号后台也有文章定时发送的功能。总而言之,延时任务的使用还是很广泛的。
延时任务的特点
- 时间有序性
- 时间具体性
- 任务中携带详细的信息 ,通常包括 任务ID, 任务的类型 ,时间点。
实现思路:
将整个Redis当做消息池,以kv形式存储消息,key为id,value为具体的消息body
使用ZSET做优先队列,按照score维持优先级(用当前时间+需要延时的时间作为score)
轮询ZSET,拿出score比当前时间戳大的数据(已过期的)
根据id拿到消息池的具体消息进行消费
消费成功,删除改队列和消息
消费失败,让该消息重新回到队列
代码实现
![项目文件结构](https://img-blog.csdnimg.cn/20210126145214473.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2MDkzMDc3,size_16,color_FFFFFF,t_70#pic_center)
1.消息模型
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Redis 消息队列中的消息体
* @author shikanatsu
*/
@Data
@Accessors(chain = true)
public class RedisMessage implements Serializable {
/** 消息队列组 **/
private String group;
/**
* 消息id
*/
private String id;
/**
* 消息延迟/ 秒
*/
@NotNull(message = "消息延时时间不能为空")
private long delay;
/**
* 消息存活时间 单位:秒
*/
@NotNull(message = "消息存活时间不能为空")
private int ttl;
/**
* 消息体,对应业务内容
*/
private Object body;
/**
* 创建时间,如果只有优先级没有延迟,可以设置创建时间为0
* 用来消除时间的影响
*/
private long createTime;
}
RedisMq 消息队列实现类
package com.shixun.base.redisMq;
import com.shixun.base.jedis.service.RedisService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Redis消息队列
*
* @author shikanatsu
*/
@Component
public class RedisMq {
/**
* 消息池前缀,以此前缀加上传递的消息id作为key,以消息{@link MSG_POOL}
* 的消息体body作为值存储
*/
public static final String MSG_POOL = "Message:Pool:";
/**
* zset队列 名称 queue
*/
public static final String QUEUE_NAME = "Message:Queue:";
// private static final int SEMIH = 30 * 60;
@Resource
private RedisService redisService;
/**
* 存入消息池
*
* @param message
* @return
*/
public boolean addMsgPool(RedisMessage message) {
if (null != message) {
redisService.set(MSG_POOL + message.getGroup() + message.getId