【深度】延时任务调度技术方案

1 背景

在实际的业务开发过程中,经常会遇到任务需要延时执行,这和定时执行有区别,定时强调的是在指定的时间点执行,延时强调的是延迟一段时间后执行,本文主要讲一下定时和延时任务的常用方案。

常见应用场景:

1 订单30分钟未支付则自动取消
2 店铺3天未上新则发送消息提醒
3 购物车里的商品降价通知
4 预订会议室,开始前10分钟提醒
5 用户注册后,3天内完善用户信息提醒

2 定时任务

Linux
* * * * *
- - - - -
| | | | |
| | | | ±---- day of week (0 - 7) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
| | | ±--------- month (1 - 12) OR jan,feb,mar,apr …
| | ±-------------- day of month (1 - 31)
| ±------------------- hour (0 - 23)
±------------------------ minute (0 - 59)

字段是否必填允许值允许特殊字符备注
Seconds0-59,-标准实现不支持此字段
Minutes0-59,-
Hours0-23,-
Day of month1-31,-
Month1–12 or JAN–DEC,-
Day of week0–7 or SUN–SAT,-0/7为周日

其中:
/ 表示间隔多久执行一次
, 表示在指定时间分别执行
* 表示每分钟,每小时…,,都要执行

定时强调的是在某个时间点开始执行,强调的是周期性。例如:

★每日凌晨1点30分统计
30 1 * * * cmd
★整点执行
0 * * * *
★每隔15分执行
*/15 * * * *
★某小时每隔15分钟执行
*/15 12 * * *
★在12点和16点,每隔15分钟执行一次
*/15 12,16 * * *

命令

crontab -l 查看
crontab -e 编辑

检查连接 https://tool.lu/crontab/

3 延时任务

2.1 MySQL表实现

将要延时执行的任务信息写入mysql,定时扫描是否有任务到期执行,具体见下图。

ID业务ID业务类型状态任务执行时间备注
11001订单02022-07-01 00:00:00七夕礼物
21002统计12022-06-01 00:00:00月度总结

潜在问题
1 延时能不能精准时间执行?

目前看做不到,crontab存在执行时间粒度问题,如果任务契合crontab的时间粒度,该方案可行

2 任务量大的时候,执行器并发控制问题

执行器的并发和业务服务的并发

3 扫库的性能问题

如果同时生效的任务不多,建议把已执行的任务归档,如果很多,则需要提前处理将要执行的任务,提前取出一段时间的任务,放入Redis缓存起来

2.2 Redis实现

实际上是使用有序集合ZSET结构来实现

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

while (true) {
	zrangebyscore key min max WITHSCORES 1
	//do something
	sleep(ts)
}

步骤
1 将延时任务加到Sorted Set,将延迟时间设为score
2 启动一个线程不断判断Sorted Set中第一个元素的score是否大于当前时间。如果大于,从Sorted Set中移除任务并添加到执行队列中,否则进行短暂休眠后重试

优点
1 时间粒度比crontab小,切可以自定义
2 Redis ZSET和内存上操作,性能更佳
3 集群和持久化保证了高可用

2.3 MQ中间件实现
2.3.1 RabbitMQ 私信队列

原理即producer为消息设置expiration值或者为队列设置x-message-ttl属性,当消息在ttl指定时间内没有被消费,则消息进入私信队列,只需启动consumer来消费私信队列即可

2.3.2 ActiveMQ scheduler

1 设置activemq schedulerSupport=“true”
2 设置message AMQ_SCHEDULED_DELAY

$ts = strtotime('2022-06-01 00:00:00');
$queue = new Queue();
return $queue->send($body, 1, $ts);
class Queue
{
	public function send($type, $params, $time) {
		......
		if ($type === self::EXEC_DELAY) {
    		$delaytime = ($time - time()) * 1000;
    		$defaultParam = ['AMQ_SCHEDULED_DELAY' => $delaytime];
    		$param = array_merge($defaultParam, $param);
    		......
		}
	}
}

发送后,在activemq的管理界面下的scheduled下,看到消息信息,见下图。

总结:延时方案很多,知其然知其不然。最符合业务的方案就是最好的方案。

参考文档
1 https://www.cnblogs.com/mfrank/p/11260355.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值