文章目录
前言
刚开始接触的项目流量小,延时关闭订单都是用的定时任务这种,访问量低,数据库压力不大。后面发现访问量高了以后定时任务对数据库压力太大了,不建议使用这种方法,所以我看了下think-queue实现延时任务的实现方法,并自己使用测试了。
下面就是具体实现方法步骤,特别简单。
一、为什么要用延时队列?
(1)最开始我用的都是定时任务(数据库轮询)。服务器搞个定时任务,几分钟跑一次。例如延迟关闭订单,去查询数据库全部待支付订单,然后把超过30分钟未支付的订单关闭。
优点:实现太简单了。
缺点:轮询时间间隔不好确定,占用服务器资源,影响数据库性能。数据库的压力会很大,尤其是订单表数目和用户大了以后,除非极个别情况,不然不建议使用(看了博客上面说的)。
我看了几个方法,对比一下其中的优劣,结合项目实际情况选择了think-queue。
(2)Redis过期回调,利用Redis的key过期回调事件,也能够实现。
我没选择他的主要原因是,一开始我觉得订阅事件会很消耗服务器性能,还有就是要修改Redis的配置,重启服务器。我们服务器挂了好多乱七八糟的项目,还有一些服务任务啥的,我担心有影响。后面看了《请勿过度依赖 Redis 的过期监听》这篇文章,才发现缺点挺致命。
Redis 不能确保 key 在指定时间被删除 , 会造成通知的延期 。
“Basically expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero.”
(3)RabbitMQ的消息队列。这个思路其实跟我要用的Redis的zset有序集合差不多。
消息在发送到消息队列服务端后并不会立马投递,而是根据消息中的属性延迟固定时间后才投递给消费者。
这个优缺点明显,优点:高效,好扩展,支持分布式,换言之就是访问高的大型项目肯定是用这个,面试也会问。
缺点:实现比前面的复杂,维护成本高。
(4)Redis的zset有序集合。
这个是在网上查到的。感觉特别简单,容易实现,对中小型项目友好。具体思路是加入一个延时队列,设置延迟时间执行。这块实现很容易,然后发布监听也很容易,于是我就用了。
二、使用步骤
1.安装Redis
服务器我用的是宝塔,所以很简单。根据自己环境安装。
要配置一下密码。
2.安装think-queue
这里我用的composer。
composer require topthink/think-queue
3.修改配置文件
我是tp6的项目,composer安装完,会在config目录下面生成queue.php的文件。
修改一下配置:
return [
'default' => 'redis', // 默认改成Redis
'connections' => [
'sync' => [
'type' => 'sync',
],
'database' => [
'type' => 'database',
'queue' => 'default',
'table' => 'jobs',
'connection' => null,
],
'redis' => [
'type' => 'redis',
'queue' => 'default', // 这个后面讲监听会说明
'host' => '127.0.0.1',
'port' => 6379, // 端口
'password' => '你的Redis密码',
'select' => 0,
'timeout' => 0,
'persistent' => false,
],
],
'failed' => [
'type' => 'none',
'table' => 'failed_jobs',
],
];
4. 写延时任务代码
在app目录下新建一个queue文件夹,然后创建一个类文件,我这里起名Order,操作订单的。
/**
* @param Job $job
* @param $order_id
* @return void
* 订单超时取消
*/
public function closeOrder(Job $job, $order_id)
{
$order = (new \app\model\Order())->getInfo($order_id, 2); // 查询当前订单是否为未支付状态
if ($order->isEmpty()) {
$job->delete(); // 非未支付状态,删除执行任务
} else {
$result = (new \app\model\Order())->overtime($order_id); // 更新订单状态为已关闭
if ($result) {
$job->delete(); //关闭成功,删除执行任务
} else {
if ($job->attempts() > 3) {
$job->delete(); // 累计执行3次均失败,删除执行任务
} else {
$job->release(10); // 间隔10秒执行一次
}
}
}
}
5.生产者发布延时任务
代码如下(示例):
public function createOrder()
{
// 创建订单业务代码
Queue::later(30 * 60, 'app\queue\Order@closeOrder', $order['id']); //加入队列,并延迟关闭时间执行
}
6.写入延迟任务后看一下Redis,是否写入数据
我本来是想找Redis查看工具的,没找到可用的,官方没有,我用的宝塔。能用的被下了,所以只能在这看。多捞啊。
7.开启进程,监听延时任务
使用php think queue:listen开启监听服务。把延迟任务设置短点,比如一分钟,过一会就能看到执行了。
我们不可能一直在服务器终端这样开启服务,所以必须要设置守护进程。
我使用的是宝塔面板,根据自己环境查找对应的服务,设置守护进程,让监听一直开着。我不太清楚这块占用服务器性能如何,知道方法后测试看看。
宝塔软件商店直接搜进程守护管理器,就找到了。
名称这里随便起,启动用户默认root即可。启动命令填“php think queue:listen”即可。进程目录选择你的项目的根目录就行。比如你项目是test,就填“/www/wwwroot/test/”。备注也是随便写。
启动命令如果填写“php think queue:listen – queue”需要再-- queue 后面加名称,而且要在queue.php文件里面修改'queue' => 'default', // 这个后面讲监听会说明
为 'queue' => 'test', // test改为你想执行的队列名称
。完整命令就是php think queue:listen -- queue test
。
可以在这查看执行日志。
总结
这个方法非常简单,适用于中小型项目,但是超大访问的分布式的项目还是要RabbitMQ、Kafka这种框架。后续学习再继续研究。有问题可以留言,我这边测试是OK的。