本文目录导读:

在PHP中实现延迟队列有几种主流方案,我来详细介绍:
基于Redis的延迟队列
使用Redis Sorted Set实现
<?php
class DelayQueue {
private $redis;
private $queueName;
public function __construct($queueName = 'delay_queue') {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
$this->queueName = $queueName;
}
/**
* 添加延迟任务
* @param string $task 任务数据
* @param int $delay 延迟秒数
*/
public function addTask($task, $delay) {
$score = time() + $delay;
return $this->redis->zAdd($this->queueName, $score, $task);
}
/**
* 获取到期的任务
*/
public function getReadyTasks() {
$now = time();
// 获取所有已到期的任务
$tasks = $this->redis->zRangeByScore($this->queueName, 0, $now);
if (!empty($tasks)) {
// 移除已获取的任务
$this->redis->zRemRangeByScore($this->queueName, 0, $now);
}
return $tasks;
}
/**
* 检查任务是否到期
*/
public function getNextTask() {
$tasks = $this->getReadyTasks();
return empty($tasks) ? null : $tasks[0];
}
}
// 使用示例
$queue = new DelayQueue();
// 添加延迟任务
$queue->addTask(json_encode(['order_id' => 123, 'action' => 'cancel']), 300); // 5分钟后执行
// 消费任务
$task = $queue->getNextTask();
if ($task) {
$data = json_decode($task, true);
// 处理任务
echo "处理订单: " . $data['order_id'];
}
使用Redis List + 定时轮询
<?php
class SimpleDelayQueue {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
/**
* 添加延迟任务
*/
public function push($queueName, $data, $delay = 0) {
$delayTime = time() + $delay;
$item = json_encode([
'data' => $data,
'execute_time' => $delayTime
]);
return $this->redis->lPush("queue:{$queueName}:waiting", $item);
}
/**
* 获取可用任务
*/
public function pop($queueName) {
$waitingKey = "queue:{$queueName}:waiting";
$readyKey = "queue:{$queueName}:ready";
// 移出到期的任务
while ($item = $this->redis->rPop($waitingKey)) {
$task = json_decode($item, true);
if ($task['execute_time'] <= time()) {
// 任务已到期,存入可执行队列
$this->redis->lPush($readyKey, json_encode($task['data']));
} else {
// 任务未到期,放回队列并退出
$this->redis->lPush($waitingKey, $item);
break;
}
}
// 返回可执行的任务
return $this->redis->rPop($readyKey);
}
}
基于消息队列(RabbitMQ)的延迟队列
安装依赖
composer require php-amqplib/php-amqplib
实现代码
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class RabbitMQDelayQueue {
private $connection;
private $channel;
public function __construct() {
$this->connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$this->channel = $this->connection->channel();
}
/**
* 投递延迟消息
* @param string $exchange 交换机
* @param string $routingKey 路由键
* @param array $data 消息数据
* @param int $delay 延迟毫秒数
*/
public function publish($exchange, $routingKey, $data, $delay) {
// 设置延迟属性
$headers = new \PhpAmqpLib\Wire\AMQPTable([
'x-delay' => $delay
]);
$msg = new AMQPMessage(json_encode($data), [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
]);
// 设置延迟插件需要的参数
$msg->set('application_headers', $headers);
// 声明延迟交换机
$this->channel->exchange_declare(
'delayed_exchange',
'x-delayed-message',
false,
true,
false,
false,
false,
new \PhpAmqpLib\Wire\AMQPTable([
'x-delayed-type' => 'direct'
])
);
$this->channel->basic_publish($msg, 'delayed_exchange', $routingKey);
}
/**
* 消费消息
*/
public function consume($queue, $callback) {
$this->channel->queue_declare($queue, false, true, false, false);
$this->channel->queue_bind($queue, 'delayed_exchange', 'delayed_routing_key');
$this->channel->basic_consume($queue, '', false, true, false, false, $callback);
while ($this->channel->is_consuming()) {
$this->channel->wait();
}
}
public function __destruct() {
$this->channel->close();
$this->connection->close();
}
}
// RabbitMQ需要安装延迟插件
// rabbitmq-plugins enable rabbitmq_delayed_message_exchange
使用数据库实现简单延迟队列
<?php
class DatabaseDelayQueue {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
/**
* 创建延迟任务表
*/
public function createTable() {
$sql = "
CREATE TABLE IF NOT EXISTS delay_tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
task_data TEXT NOT NULL,
execute_at INT NOT NULL,
status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_execute_at (execute_at),
INDEX idx_status (status)
)
";
$this->db->exec($sql);
}
/**
* 添加延迟任务
*/
public function addTask($data, $delay) {
$stmt = $this->db->prepare(
"INSERT INTO delay_tasks (task_data, execute_at) VALUES (?, ?)"
);
return $stmt->execute([
json_encode($data),
time() + $delay
]);
}
/**
* 获取可执行任务并锁定
*/
public function getAvailableTasks($limit = 10) {
$this->db->beginTransaction();
try {
// 使用行级锁或乐观锁
$stmt = $this->db->prepare(
"SELECT * FROM delay_tasks
WHERE execute_at <= ? AND status = 'pending'
ORDER BY execute_at ASC
LIMIT ? FOR UPDATE"
);
$stmt->execute([time(), $limit]);
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($tasks)) {
$ids = array_column($tasks, 'id');
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$updateStmt = $this->db->prepare(
"UPDATE delay_tasks SET status = 'processing' WHERE id IN ($placeholders)"
);
$updateStmt->execute($ids);
}
$this->db->commit();
return $tasks;
} catch (Exception $e) {
$this->db->rollBack();
throw $e;
}
}
/**
* 完成任务
*/
public function completeTask($id) {
$stmt = $this->db->prepare(
"UPDATE delay_tasks SET status = 'completed' WHERE id = ?"
);
return $stmt->execute([$id]);
}
}
使用第三方库(推荐)
Laravel 队列
// Laravel 内置延迟队列支持
dispatch(new ProcessPodcast($podcast))
->delay(now()->addMinutes(10));
// 或使用 Job
ProcessPodcast::dispatch($podcast)
->onQueue('processing')
->delay(now()->addMinutes(10));
Yii2 队列扩展
// 使用 yiisoft/yii2-queue
class DelayTask extends \yii\base\BaseObject implements \yii\queue\JobInterface
{
public $data;
public function execute($queue)
{
// 处理任务
}
}
// 延迟执行
Yii::$app->queue->delay(1800)->push(new DelayTask([
'data' => 'delayed task'
]));
最佳实践建议
-
生产环境推荐方案
- 小规模项目:Redis Sorted Set
- 中大型项目:RabbitMQ + 延迟插件
- 微服务架构:Kafka + 时间轮
-
注意事项
- 任务幂等性处理
- 失败重试机制
- 数据持久化
- 监控告警
-
性能优化
- 使用批量处理
- 合理设置轮询间隔
- 避免数据库锁表
选择合适的方案要根据项目规模、团队技术栈和运维能力来决定。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播