基于redis的异步延时任务
项目中有时候经常用到到特定时间去执行的任务,比如订单15分钟取消,3天发货提醒等等,如果利用crontab直接去查找数据库数据有点太过浪费,因而考虑使用redis去存储这些任务,然后crontab执行,效果就好得多。
废话不多说,上代码。
// 异步延时任务抽象类,所有异步延时任务继承他
<?php
/**
* User: 13sai
* Date: 2018/9/1
* Time: 16:35
*/
namespace DelayTask;
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();
}
// 测试类,继承抽象类
<?php
/**
* User: 13sai
* Date: 2018/9/1
* Time: 17:55
*/
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 "写入成功。<br />";
}
}
}
复制代码
其中有个RedisManager,简单封装了一下redis操作,主要利用了有序集合。
<?php
/**
* User: 13sai
* Date: 2018/9/1
* Time: 16:36
*/
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()
{
}
}
复制代码
插入延时任务:
(new TestDelayTask(4))->startAfter(120);
(new TestDelayTask(3))->startAt(153120384);
复制代码
我没创建一个任务,来执行上面的测试类:
<?php
/**
* User: 13sai
* Date: 2018/9/5
* Time: 12:00
*/
class DelayTaskTask
{
const QueneName = 'delayTask';
private $currentTime;
private $once = 5;
public function run()
{
$this->currentTime = time();
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;
}
}
}
}
复制代码
我没只需要创建一个任务,每分钟执行上面的DelayTaskTask的run方法即可。
tips:注意每次插入任务的实例务必不同,否则在redis会被覆盖掉。