← Назад к справке

Ядро

Агенты, очереди и фоновые задачи

Рабочая справка по фоновым задачам в 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(),
            ]);
        }
    }
}

Это не промышленная очередь, а простой рабочий шаблон. Для сложных задач можно добавить блокировки, повторные попытки, приоритет, дату следующей попытки и отдельный лог ошибок.

Источники