Очередь задач на PHP на Doctrine2
Бывают ситуации когда нет смысла использовать серверы очередей, типа Rabbitmq, German, etc. Была задача — отправка писем пользователям, проверка на поступление оплаты.
Для этого было решено написать простой класс, который считывает сообщения из таблицы, удаляет сообщения, добавлят задачу в таблицу.
На тот момент я решил задачу с помощью Doctrine 2 + CI 2. Надеюсь, что общий принцип будет понятен, и это решение вы сможете подстроить под себя.
Начнём с модели:
<?php
namespace Entity;
/**
* Описание модели "Задачи для робота"
*
* @Entity
* @Table(name="tasks")
*/
class Task extends BaseEntity {
/**
* @Column(type="string", length=115, nullable=true)
*/
protected $type;
/**
* @Column(type="array", nullable=true)
*/
protected $params;
/**
* @Column(type="boolean", nullable=true)
*/
protected $status = false;
public function __construct()
{
}
/**
* Тип задачи (отправка рассылки, отправка письма, обновление статуса конкурса)
*
* @return string
*/
public function getType()
{
return $this->type;
}
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Параметры для выполнения (это массив, который будет передан в метод для выполнения)
* @return array
*/
public function getParams()
{
return $this->params;
}
public function setParams($params)
{
$this->params = $params;
return $this;
}
/**
* Статус задачи (выполнена, не выполнена)
*
* @return string
*/
public function getStatus()
{
return $this->status;
}
public function setStatus($status)
{
$this->status = $status;
return $this;
}
}
setParams
— добавляет необходимые параметры к задаче ввиде массива, преобразованием массива занимается Doctrine. Вы можете сериализовать массив, сохранить, затем десериализовать — serialize
Теперь, добавим нужную задачу:
$hello_world_task = new Entity\Task;
$hello_world_task->setType('hello_world')->setParams(['key1' => 'value1', 'key2' => 'value2']);
$this->tasks->addTask($hello_world_task);
Создадим контроллер, который будет запускаться cron
-ом:
class Robot extends CI_Controller
{
public $doctrine;
public function __construct()
{
parent::__construct();
if (php_sapi_name() !== 'cli') {
show_404();
} else {
$this->load->library(['doctrine', 'tasks']);
$this->doctrine = $this->doctrine->em;
}
}
/**
* Запуск задач
*/
public function run_tasks()
{
$tasks = $this->tasks->getTasks();
foreach ($tasks as $task) {
switch ($task->getType()) {
// Отправка рассылки
case 'hello_world':
$this->hello_world_worker($task);
break;
}
}
}
private function hello_world_worker(\Entity\Task $task)
{
$params = $task->getParams();
foreach ($params as $key => $value) {
echo "$key - $value";
}
$this->tasks->deleteTask($task);
}
}
Теперь осталось добавить задачу в cron
, */15 * * * * php /path/to/index.php robot run_tasks
И добавим саму библиотеку, в папку libraries
:
class Tasks {
private $ci;
public function __construct($params = null)
{
$this->ci =& get_instance();
}
public function getTasks()
{
$tasks = $this->ci->doctrine
->getRepository('Entity\Task')
->findBy(array('status' => false));
return $tasks;
}
public function addTask(\Entity\Task $task)
{
$this->ci->doctrine->persist($task);
$this->ci->doctrine->flush();
return $this;
}
public function deleteTask(\Entity\Task $task)
{
$this->ci->doctrine->remove($task);
$this->ci->doctrine->flush();
return $this;
}
}