在CodeIgniter框架中处理队列任务,核心是将耗时操作从主请求流程中剥离,先记录任务信息,再由独立的进程异步执行,这样能避免用户请求等待耗时操作完成,提升系统整体体验。常见的实现方式有两种,一种是轻量级的基于数据库的任务队列,适合中小型项目;另一种是结合Redis、RabbitMQ等第三方消息队列服务,适合高并发场景。

方案一:基于数据库的简单队列实现
这种方式不需要引入额外的服务,仅通过数据库表存储任务信息,适合任务量不大、对实时性要求不高的场景。
1. 创建任务队列表
首先需要在数据库中创建一张存储队列任务的表,结构如下:
CREATE TABLE `ci_queue_tasks` ( `id` int(11) NOT NULL AUTO_INCREMENT, `task_type` varchar(50) NOT NULL COMMENT '任务类型,比如send_email、generate_report', `task_data` text NOT NULL COMMENT '任务相关数据,json格式存储', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待处理 1处理中 2处理完成 3处理失败', `create_time` int(11) NOT NULL COMMENT '任务创建时间戳', `update_time` int(11) DEFAULT NULL COMMENT '任务更新时间戳', `retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '重试次数', PRIMARY KEY (`id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 封装队列任务操作类
在CodeIgniter的libraries目录下创建Queue类,封装任务的添加、获取、更新状态等方法:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Queue {
protected $CI;
protected $table = 'ci_queue_tasks';
public function __construct() {
$this->CI =& get_instance();
$this->CI->load->database();
}
/**
* 添加队列任务
* @param string $task_type 任务类型
* @param array $task_data 任务数据
* @return int 新增任务ID
*/
public function add_task($task_type, $task_data) {
$data = array(
'task_type' => $task_type,
'task_data' => json_encode($task_data, JSON_UNESCAPED_UNICODE),
'status' => 0,
'create_time' => time(),
'retry_count' => 0
);
$this->CI->db->insert($this->table, $data);
return $this->CI->db->insert_id();
}
/**
* 获取待处理的任务
* @param int $limit 每次获取的任务数量
* @return array 任务列表
*/
public function get_pending_tasks($limit = 10) {
$this->CI->db->where('status', 0);
$this->CI->db->order_by('create_time', 'ASC');
$this->CI->db->limit($limit);
$query = $this->CI->db->get($this->table);
return $query->result_array();
}
/**
* 更新任务状态
* @param int $task_id 任务ID
* @param int $status 目标状态
* @param string $error_msg 错误信息,失败时记录
* @return bool 是否更新成功
*/
public function update_task_status($task_id, $status, $error_msg = '') {
$update_data = array(
'status' => $status,
'update_time' => time()
);
if ($status == 3 && $error_msg != '') {
$update_data['task_data'] = json_encode(array('error' => $error_msg), JSON_UNESCAPED_UNICODE);
}
$this->CI->db->where('id', $task_id);
return $this->CI->db->update($this->table, $update_data);
}
/**
* 增加任务重试次数
* @param int $task_id 任务ID
* @return bool 是否更新成功
*/
public function increment_retry($task_id) {
$this->CI->db->set('retry_count', 'retry_count+1', FALSE);
$this->CI->db->where('id', $task_id);
return $this->CI->db->update($this->table);
}
}
3. 业务层添加任务
在需要添加队列任务的地方,比如用户注册后发送欢迎邮件,调用Queue类添加任务:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class User extends CI_Controller {
public function register() {
// 假设用户注册逻辑已完成,获取到用户邮箱
$user_email = 'test@ipipp.com';
// 加载队列库
$this->load->library('queue');
// 添加发送邮件的队列任务
$task_data = array(
'email' => $user_email,
'subject' => '欢迎注册',
'content' => '感谢您的注册,祝您使用愉快'
);
$task_id = $this->queue->add_task('send_email', $task_data);
if ($task_id > 0) {
echo '注册成功,邮件将在后台发送';
} else {
echo '注册成功,但邮件任务添加失败';
}
}
}
4. 编写命令行脚本处理任务
CodeIgniter支持命令行运行控制器,我们可以创建一个命令行控制器来处理队列任务,然后通过定时任务或者守护进程调用这个控制器。
首先在application/controllers目录下创建Cli目录,然后创建Queue_worker.php:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Queue_worker extends CI_Controller {
public function __construct() {
parent::__construct();
// 仅允许命令行访问
if (!$this->input->is_cli_request()) {
exit('该控制器仅允许命令行访问');
}
$this->load->library('queue');
}
/**
* 处理队列任务的主方法
*/
public function run() {
$max_retry = 3; // 最大重试次数
while (true) {
// 获取待处理任务
$tasks = $this->queue->get_pending_tasks(10);
if (empty($tasks)) {
// 没有任务时休眠5秒
sleep(5);
continue;
}
foreach ($tasks as $task) {
// 标记任务为处理中
$this->queue->update_task_status($task['id'], 1);
$task_data = json_decode($task['task_data'], TRUE);
$result = FALSE;
try {
// 根据任务类型处理不同任务
switch ($task['task_type']) {
case 'send_email':
$result = $this->handle_send_email($task_data);
break;
case 'generate_report':
$result = $this->handle_generate_report($task_data);
break;
default:
throw new Exception('未知的任务类型');
}
} catch (Exception $e) {
// 处理失败,记录错误
$this->queue->update_task_status($task['id'], 3, $e->getMessage());
// 如果重试次数未达上限,重置状态为待处理
if ($task['retry_count'] < $max_retry) {
$this->queue->increment_retry($task['id']);
$this->queue->update_task_status($task['id'], 0);
}
continue;
}
// 处理成功,标记任务完成
if ($result) {
$this->queue->update_task_status($task['id'], 2);
} else {
$this->queue->update_task_status($task['id'], 3, '任务处理返回失败');
}
}
}
}
/**
* 处理发送邮件任务
* @param array $data 任务数据
* @return bool 是否处理成功
*/
private function handle_send_email($data) {
$this->load->library('email');
$this->email->from('system@ipipp.com', '系统通知');
$this->email->to($data['email']);
$this->email->subject($data['subject']);
$this->email->message($data['content']);
return $this->email->send();
}
/**
* 处理生成报表任务
* @param array $data 任务数据
* @return bool 是否处理成功
*/
private function handle_generate_report($data) {
// 这里写生成报表的具体逻辑,比如查询数据、生成excel等
// 示例仅返回true模拟成功
return TRUE;
}
}
然后通过命令行启动队列 worker:
php /path/to/your/codeigniter/index.php cli/queue_worker run
如果需要后台常驻运行,可以配合nohup或者supervisor工具管理进程。
方案二:结合Redis实现队列
如果项目并发量较高,基于数据库的队列可能存在性能瓶颈,这时候可以使用Redis的list结构实现队列,Redis的lpush和rpop操作是原子性的,性能更好。
1. 安装Redis扩展并配置
首先确保服务器安装了Redis,并且PHP安装了redis扩展,然后在CodeIgniter的config目录下创建redis.php配置文件:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$config['redis_host'] = '127.0.0.1';
$config['redis_port'] = 6379;
$config['redis_password'] = '';
$config['redis_database'] = 0;
$config['redis_timeout'] = 0;
2. 封装Redis队列操作类
在libraries目录下创建Redis_queue类:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Redis_queue {
protected $CI;
protected $redis;
protected $queue_key = 'ci_queue_tasks';
public function __construct() {
$this->CI =& get_instance();
$this->CI->config->load('redis', TRUE);
$redis_config = $this->CI->config->item('redis');
$this->redis = new Redis();
$this->redis->connect($redis_config['redis_host'], $redis_config['redis_port'], $redis_config['redis_timeout']);
if ($redis_config['redis_password'] != '') {
$this->redis->auth($redis_config['redis_password']);
}
$this->redis->select($redis_config['redis_database']);
}
/**
* 添加任务到队列
* @param string $task_type 任务类型
* @param array $task_data 任务数据
* @return int 队列长度
*/
public function push_task($task_type, $task_data) {
$task = array(
'task_type' => $task_type,
'task_data' => $task_data,
'create_time' => time()
);
return $this->redis->lPush($this->queue_key, json_encode($task, JSON_UNESCAPED_UNICODE));
}
/**
* 从队列获取任务
* @param int $timeout 阻塞超时时间,0表示不阻塞
* @return array|bool 任务信息,没有任务返回false
*/
public function pop_task($timeout = 0) {
if ($timeout > 0) {
$result = $this->redis->brPop(array($this->queue_key), $timeout);
if (empty($result)) {
return FALSE;
}
$task_json = $result[1];
} else {
$task_json = $this->redis->rPop($this->queue_key);
}
if (!$task_json) {
return FALSE;
}
return json_decode($task_json, TRUE);
}
}
3. 业务层添加任务
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Order extends CI_Controller {
public function create() {
// 订单创建逻辑
$order_id = 123;
$this->load->library('redis_queue');
// 添加订单后续处理任务,比如扣库存、发通知
$task_data = array('order_id' => $order_id);
$this->redis_queue->push_task('handle_order', $task_data);
echo '订单创建成功';
}
}
4. 编写Redis队列 worker
同样创建命令行控制器处理Redis队列任务:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Redis_queue_worker extends CI_Controller {
public function __construct() {
parent::__construct();
if (!$this->input->is_cli_request()) {
exit('该控制器仅允许命令行访问');
}
$this->load->library('redis_queue');
}
public function run() {
while (true) {
// 阻塞获取任务,超时时间30秒
$task = $this->redis_queue->pop_task(30);
if (!$task) {
continue;
}
try {
switch ($task['task_type']) {
case 'handle_order':
$this->handle_order($task['task_data']);
break;
default:
throw new Exception('未知任务类型');
}
} catch (Exception $e) {
// 记录错误日志
log_message('error', '队列任务处理失败:' . $e->getMessage());
}
}
}
private function handle_order($data) {
// 处理订单的具体逻辑
return TRUE;
}
}
两种方案对比
两种方案各有适用场景,具体对比如下:
| 对比项 | 数据库队列 | Redis队列 |
|---|---|---|
| 实现复杂度 | 低,无需额外服务 | 中等,需要安装Redis |
| 性能 | 一般,适合低并发 |
CodeIgniter队列任务后台任务处理消息队列修改时间:2026-07-23 23:10:03