php. 延时任务,PHP DIY 系列------应用篇:2. 延时任务

延时任务有别于定时任务,定时任务往往是固定周期的,有明确的触发时间。而延时任务一般没有固定的开始时间,它常常是由一个事件触发的,而在这个事件触发之后的一段时间内触发另一个事件。

我们不妨来设定一个实际的场景,电商系统下单成功之后如果15分钟未支付成功,就系统自动取消订单。

我们先来实现代码,然后再来详细说明:

// 我们定义一个abstract,定义两个方法,startAfter和startAt

abstract class DelayTask

{

const DELAY_TASK = 'delayTask';

/**

* 延时时间,在触发时间后多久执行

* @param $pushDelayTime

*/

public function startAfter(int $pushDelayTime)

{

RedisManager::getRedis()->zAdd(self::DELAY_TASK, time() + $pushDelayTime, serialize($this));

}

/**

* 定时时间,在未来某时刻执行

* @param $pushDelayAt

*/

public function startAt(int $pushDelayAt)

{

RedisManager::getRedis()->zAdd(self::DELAY_TASK, $pushDelayAt, serialize($this));

}

abstract function run();

}

class TestDelayTask extends DelayTask

{

public function __construct($id)

{

$this->id = $id;

}

public function run()

{

$file = 'text.txt';//要写入文件的文件名(可以是任意文件名),如果文件不存在,将会创建一个

$content = "写入的内容".time()."\n";

if($f = file_put_contents($file, $content,FILE_APPEND)){// 这个函数支持版本(PHP 5)

echo "写入成功。
";

}

}

}

RedisManager是封装的一个单例模式实现的redis类,我们也贴出代码,然后再对上面的代码做一些说明。

class RedisManager

{

private static $instance = null;

private function __construct()

{

self::$instance = new \Redis();

$config = require 'redis.config.php';

self::$instance->connect($config['host'], $config['port'], $config['timeout']);

if (isset($config['password'])) {

self::$instance->auth($config['password']);

}

}

/**

* 获取静态实例

*/

public static function getRedis()

{

if (!self::$instance) {

new self;

}

return self::$instance;

}

/**

* 禁止clone

*/

private function __clone()

{

}

}

有序集合

redis 127.0.0.1:6379> ZADD saif 1 redis

(integer) 1

redis 127.0.0.1:6379> ZADD saif 2 mongodb

(integer) 1

redis 127.0.0.1:6379> ZADD saif 4 mysql

(integer) 0

redis 127.0.0.1:6379> ZRANGE saif 0 10 WITHSCORES

1) "redis"

2) "1"

3) "mongodb"

4) "2"

5) "mysql"

6) "4"

如果你有redis可视化工具,你会发现有序集合存储的结构是这样:

row

value

score

1

redis

1

2

mongodb

2

3

mysql

4

我们再来看一下代码,我们使用时间戳作为分值,使用对象作为值,存储到Redis有序集合。

file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) : int — 将一个字符串写入文件

执行

我们先尝试写入任务:

(new TestDelayTask(4))->startAfter(900);

(new TestDelayTask(4))->startAfter(120);

(new TestDelayTask(4))->startAfter(600);

(new TestDelayTask(3))->startAt(158120384);

(new TestDelayTask(3))->startAt(158420380);

(new TestDelayTask(3))->startAt(158120900);

执行代码:

class DelayTaskTask

{

const QueneName = 'delayTask';

private $currentTime;

private $once = 5;

public function run()

{

$this->currentTime = time();

error_reporting(error_reporting() & ~E_WARNING);

while (true) {

// 每次取出5条

$list = RedisManager::getRedis()->zRange(self::QueneName, 0, $this->once, true);

if (!empty($list)) {

foreach ($list as $val=>$score) {

if ($score < $this->currentTime) {

unserialize($val)->run();

RedisManager::getRedis()->zDelete(self::QueneName, $val);

} else {

break 2;

}

}

} else {

break;

}

}

}

}

(new DelayTaskTask())->run();

我们可以设置一个定时任务,每分钟执行一次上述代码。

我们执行之后,会发现一旦过了我们设定的时间,text.txt就不断有文字写入了。

代码比较杂,我有一个demo代码,大家可以查看。

这样,我们就利用Redis有序集合,完成了一个很基础的延时任务。

问题

假如run方法代码执行时间过长,一分钟执行一次有什么问题吗?

每分钟执行一次,间隔有点长,能不能优化呢?

本作品采用《CC 协议》,转载必须注明作者和本文链接

收藏前不妨点个赞试试!!!

分享开发知识,欢迎交流。qq交流群:965666112,公众号:程序员涛子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值