本文目录导读:

在PHP中实现异步任务队列,主要有以下几种方案:
Redis + 队列(最常用)
生产者(提交任务)
// 使用 Redis 列表作为队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 添加任务到队列
$taskData = [
'type' => 'send_email',
'data' => [
'to' => 'user@example.com',
'subject' => 'Welcome',
'body' => 'Hello World'
],
'created_at' => time()
];
$redis->lpush('task_queue', json_encode($taskData));
echo "任务已提交\n";
消费者(处理任务)
// worker.php - 后台常驻进程
while (true) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 阻塞式获取任务(BRPOP 会一直等待直到有任务)
$task = $redis->brpop('task_queue', 0);
if ($task) {
$taskData = json_decode($task[1], true);
// 处理任务
try {
handleTask($taskData);
echo "[" . date('Y-m-d H:i:s') . "] 任务完成: " . $taskData['type'] . "\n";
} catch (Exception $e) {
// 记录错误日志
$redis->lpush('failed_tasks', json_encode([
'task' => $taskData,
'error' => $e->getMessage(),
'failed_at' => date('Y-m-d H:i:s')
]));
echo "任务失败: " . $e->getMessage() . "\n";
}
}
}
function handleTask($taskData) {
switch ($taskData['type']) {
case 'send_email':
// 发送邮件逻辑
sleep(2); // 模拟耗时操作
break;
case 'process_image':
// 图片处理逻辑
break;
default:
throw new Exception("未知任务类型");
}
}
使用 Beanstalkd
// 安装: composer require pda/pheanstalk
use Pheanstalk\Pheanstalk;
// 生产者
$pheanstalk = Pheanstalk::create('127.0.0.1:11300');
$tube = 'email_jobs';
$data = json_encode([
'to' => 'user@example.com',
'subject' => 'Test'
]);
$pheanstalk->useTube($tube)->put($data, 1024, 60); // 优先级, 延迟
// 消费者
while (true) {
$job = $pheanstalk->watch('email_jobs')->reserve();
$data = json_decode($job->getData(), true);
try {
// 处理任务
processEmail($data);
$pheanstalk->delete($job); // 删除完成任务
} catch (Exception $e) {
$pheanstalk->bury($job); // 任务失败放入bury
}
}
使用 RabbitMQ
// 安装: composer require php-amqplib/php-amqplib
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
// 生产者
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
$data = json_encode(['task' => 'send_email', 'params' => ['to' => 'a@b.com']]);
$msg = new AMQPMessage($data, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);
$channel->basic_publish($msg, '', 'task_queue');
$channel->close();
$connection->close();
// 消费者
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
$callback = function($msg) {
$data = json_decode($msg->body, true);
echo "处理任务: " . $data['task'] . "\n";
// 处理逻辑
sleep(1);
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
完整的异步任务管理系统
<?php
// TaskQueue.php - 完整任务队列管理系统
class TaskQueue {
private $redis;
private $queueName;
private $failedQueue;
public function __construct($host = '127.0.0.1', $port = 6379) {
$this->redis = new Redis();
$this->redis->connect($host, $port);
$this->queueName = 'task_queue';
$this->failedQueue = 'failed_queue';
}
// 添加任务
public function addTask($type, $data, $priority = 0) {
$task = [
'id' => uniqid('task_', true),
'type' => $type,
'data' => $data,
'created_at' => time(),
'priority' => $priority,
'attempts' => 0
];
// 使用有序集合存储任务,优先级作为score
$this->redis->zadd($this->queueName, $priority, json_encode($task));
return $task['id'];
}
// 获取下一个任务(带优先级)
public function getNextTask() {
$tasks = $this->redis->zrangebyscore($this->queueName, '-inf', '+inf', ['limit' => [0, 1]]);
if (empty($tasks)) {
return null;
}
$taskJson = $tasks[0];
$this->redis->zrem($this->queueName, $taskJson);
return json_decode($taskJson, true);
}
// 处理任务(带重试机制)
public function processTask($task) {
$task['attempts']++;
try {
switch ($task['type']) {
case 'email':
$this->sendEmail($task['data']);
break;
case 'image':
$this->processImage($task['data']);
break;
case 'notification':
$this->sendNotification($task['data']);
break;
default:
throw new Exception("未知任务类型");
}
// 任务成功
$this->logSuccess($task);
return true;
} catch (Exception $e) {
// 重试逻辑
if ($task['attempts'] < 3) {
$this->retryTask($task);
} else {
$this->handleFailure($task, $e->getMessage());
}
return false;
}
}
// 重试任务
private function retryTask($task) {
// 延迟重试
$delay = $task['attempts'] * 30; // 逐次增加延迟
$score = time() + $delay;
$this->redis->zadd('delayed_queue', $score, json_encode($task));
$this->logRetry($task);
}
// 处理失败任务
private function handleFailure($task, $error) {
$task['error'] = $error;
$this->redis->lpush($this->failedQueue, json_encode($task));
$this->logFailure($task, $error);
}
// 发送邮件
private function sendEmail($data) {
// 邮件发送逻辑
sleep(1);
echo "发送邮件到: " . $data['to'] . "\n";
}
// 处理图片
private function processImage($data) {
// 图片处理逻辑
sleep(2);
echo "处理图片: " . $data['path'] . "\n";
}
// 发送通知
private function sendNotification($data) {
// 通知发送逻辑
sleep(1);
echo "发送通知: " . $data['message'] . "\n";
}
// 日志记录
private function logSuccess($task) {
$logData = json_encode([
'task_id' => $task['id'],
'type' => $task['type'],
'status' => 'success',
'time' => date('Y-m-d H:i:s')
]);
$this->redis->lpush('task_logs', $logData);
}
private function logFailure($task, $error) {
$logData = json_encode([
'task_id' => $task['id'],
'type' => $task['type'],
'status' => 'failed',
'error' => $error,
'time' => date('Y-m-d H:i:s')
]);
$this->redis->lpush('task_logs', $logData);
}
private function logRetry($task) {
echo "任务 {$task['id']} 重试,第 {$task['attempts']} 次\n";
}
// 启动 Worker
public function runWorker() {
echo "Worker 开始运行...\n";
while (true) {
// 处理延迟队列
$this->processDelayedTasks();
// 获取并处理任务
$task = $this->getNextTask();
if ($task) {
echo "[" . date('Y-m-d H:i:s') . "] 处理任务: " . $task['type'] . "\n";
$this->processTask($task);
} else {
// 没有任务时休息一下
usleep(500000); // 0.5秒
}
}
}
// 处理延迟任务
private function processDelayedTasks() {
$now = time();
$tasks = $this->redis->zrangebyscore('delayed_queue', '-inf', $now);
foreach ($tasks as $taskJson) {
$this->redis->zrem('delayed_queue', $taskJson);
$task = json_decode($taskJson, true);
$this->redis->zadd($this->queueName, 0, $taskJson);
echo "任务 {$task['id']} 延迟到期,重新入队\n";
}
}
}
// 使用示例
// 提交任务
$taskQueue = new TaskQueue();
$taskId = $taskQueue->addTask('email', ['to' => 'user@example.com', 'subject' => 'Test']);
echo "任务已提交: $taskId\n";
// 启动 Worker
// $taskQueue->runWorker();
使用成熟的队列系统
Laravel Queue
// 在 Laravel 中使用
class SendEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function handle()
{
// 处理逻辑
Mail::to($this->data['to'])->send(new WelcomeMail());
}
}
// 提交任务
SendEmailJob::dispatch($data)->onQueue('emails');
监控与管理
class TaskMonitor {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
// 获取队列状态
public function getQueueStatus() {
return [
'pending' => $this->redis->zcard('task_queue'),
'failed' => $this->redis->llen('failed_queue'),
'delayed' => $this->redis->zcard('delayed_queue')
];
}
// 查看最近日志
public function getRecentLogs($count = 50) {
return $this->redis->lrange('task_logs', 0, $count);
}
// 重发失败任务
public function retryFailedTasks() {
while ($taskJson = $this->redis->rpop('failed_queue')) {
$task = json_decode($taskJson, true);
unset($task['error']);
$task['attempts'] = 0;
$this->redis->zadd('task_queue', 0, json_encode($task));
}
}
}
启动 Worker
# 启动单个 Worker php worker.php # 启动多个 Worker(并发) nohup php worker.php & nohup php worker.php & # 使用 supervisor 管理 # /etc/supervisor/conf.d/worker.conf [program:task_worker] command=php /path/to/worker.php numprocs=4 process_name=%(program_name)s_%(process_num)02d autostart=true autorestart=true user=www-data redirect_stderr=true
最佳实践
- 使用成熟方案:优先选择 Laravel Queues 或 RabbitMQ
- 持久化:确保任务不丢失
- 重试机制:重要任务要实现重试
- 监控告警:统计失败率并设置告警
- 并发控制:合理设置 Worker 数量
- 任务超时:设置合理的超时时间
选择哪种方案取决于你的具体需求:
- 简单场景:Redis + 队列
- 复杂需求:RabbitMQ/Beanstalkd
- 使用框架:Laravel Queues
- 需要配置管理:Beanstalkd
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播