Ядро
Агенты, очереди и фоновые задачи
Рабочая справка по фоновым задачам в Bitrix Framework: CAgent, cron, CheckAgents и простая очередь на своей таблице.
Агенты подходят для регулярных задач. Если нужно обработать много записей, лучше не делать всё за один запуск, а завести очередь: агент берёт небольшую пачку, обрабатывает и выходит.
Агенты
Агент — это PHP-вызов, который Битрикс выполняет по расписанию.
Что такое агент
Агент хранится в системе как строка PHP-кода. Если функция агента возвращает строку следующего вызова, агент продолжит выполняться. Если возвращает пустую строку — агент остановится.
Добавить агент
<?php
const MODULE_ID = 'test.example';
/**
* Добавляет агент модуля.
*/
function addQueueWorkerAgent(): int
{
return (int) \CAgent::AddAgent(
'\\test\\Example\\Agent\\QueueWorkerAgent::run();',
MODULE_ID,
'N',
300,
'',
'Y',
'',
100,
false,
true
);
}
$agent_id = addQueueWorkerAgent();
echo $agent_id; Функция агента
<?php
namespace test\Example\Agent;
use Bitrix\Main\Diag\Debug;
class QueueWorkerAgent
{
/**
* Выполняет агент и возвращает следующий вызов.
*/
public static function run(): string
{
try {
// Здесь обработка фоновой задачи
} catch (\Throwable $exception) {
Debug::dumpToFile(
[
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
],
'queue_worker_error',
'/local/logs/queue_worker.log'
);
}
return '\\test\\Example\\Agent\\QueueWorkerAgent::run();';
}
} Cron
На боевом портале фоновые задачи лучше запускать через cron.
Запуск на хитах и cron
| Режим | Что означает |
|---|---|
| На хитах | Агенты проверяются при посещении сайта. |
| Через cron | Проверку запускает серверное расписание. |
*/10 * * * * /usr/bin/php -f /home/bitrix/www/bitrix/php_interface/cron_events.php Проверить агенты руками
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
/**
* Принудительно проверяет агенты.
*/
function runAgentsCheck(): void
{
\CAgent::CheckAgents();
}
runAgentsCheck();
echo 'Agents checked'; Очереди
В простом варианте очередь можно сделать на своей таблице: записи лежат в статусе
new, агент берёт пачку и переводит их в done или
error.
Когда нужна очередь
- нужно обработать много CRM-карточек;
- нельзя держать один долгий PHP-процесс;
- нужно повторять ошибочные задачи;
- нужно видеть статус обработки;
- нужно ограничить нагрузку на портал.
Простая таблица очереди
<?php
namespace test\Example\Model;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields;
class QueueTable extends DataManager
{
/**
* Возвращает имя таблицы.
*/
public static function getTableName(): string
{
return 'b_test_example_queue';
}
/**
* Возвращает карту полей.
*/
public static function getMap(): array
{
return [
new Fields\IntegerField('ID', [
'primary' => true,
'autocomplete' => true,
]),
new Fields\StringField('STATUS', [
'required' => true,
]),
new Fields\TextField('PAYLOAD'),
new Fields\IntegerField('RETRY_COUNT'),
new Fields\DatetimeField('CREATED_AT'),
new Fields\DatetimeField('UPDATED_AT'),
];
}
} Агент-воркер
<?php
namespace test\Example\Agent;
use Bitrix\Main\Type\DateTime;
use test\Example\Model\QueueTable;
class QueueWorkerAgent
{
public const LIMIT = 20;
/**
* Запускает обработку очереди.
*/
public static function run(): string
{
$tasks = self::fetchNewTasks();
foreach ($tasks as $task) {
self::processTask($task);
}
return '\\test\\Example\\Agent\\QueueWorkerAgent::run();';
}
/**
* Получает новые задачи очереди.
*/
private static function fetchNewTasks(): array
{
return QueueTable::getList([
'select' => [
'ID',
'STATUS',
'PAYLOAD',
'RETRY_COUNT',
],
'filter' => [
'=STATUS' => 'new',
],
'order' => [
'ID' => 'ASC',
],
'limit' => self::LIMIT,
])->fetchAll();
}
/**
* Обрабатывает одну задачу очереди.
*/
private static function processTask(array $task): void
{
$task_id = (int) $task['ID'];
try {
QueueTable::update($task_id, [
'STATUS' => 'done',
'UPDATED_AT' => new DateTime(),
]);
} catch (\Throwable $exception) {
QueueTable::update($task_id, [
'STATUS' => 'error',
'RETRY_COUNT' => (int) $task['RETRY_COUNT'] + 1,
'UPDATED_AT' => new DateTime(),
]);
}
}
} Это не промышленная очередь, а простой рабочий шаблон. Для сложных задач можно добавить блокировки, повторные попытки, приоритет, дату следующей попытки и отдельный лог ошибок.