Нужна ли мне задача cron для обработки очереди?

32

У меня есть задача, которая занимает около 45 минут и должна выполняться каждый день (синхронизация пользователей с несколькими внешними базами данных и т. Д.).

Чтобы справиться с работой, я настроил очередь cron hook_cron_queue_info()следующим образом:

function mymodule_cron_queue_info() {
  $queues = array();
  $queues['update_users_queue'] = array(
    'worker callback' => '_mymodule_process_user_queue_item',
    'time' => 120,
  );
  return $queues;
}

Я заполняю очередь, используя эту функцию:

function mymodule_queue_all_users_for_synching() {
  //...query for users...

  $queue = DrupalQueue::get('update_users_queue');
  foreach($users as $user) {
    $queue->createItem($user);
  }
}

Функция заполнения очереди вызывается как задача cron. Я использую Elysia Cron , поэтому моя реализация hook_cronapi():

function mymodule_cronapi($op, $job = NULL) {
  $items = array();
  $items['queue_users_for_synch'] = array(
    'description' => 'Queue all user accounts for synching.',
    'rule' => '0 3 * * *', // Run this job every day at 3am.
    'callback' => 'mymodule_queue_all_users_for_synching',
  );
  return $items;
}

Рабочая функция для каждого элемента очереди, определенного в mymodule_cron_queue_info:

function _mymodule_process_user_queue_item($item) {
  //...synchronize user ($item)...
}

У меня вопрос, когда Cron действительно начнет обрабатывать очередь?

Скажем, я заполняю очередь каждый день в 3 часа ночи и хочу обработать ее 120 секунд из каждых 30 минут, пока она не будет завершена - нужно ли создавать еще одну задачу cron?

joe_flash
источник
Я должен упомянуть, что я использую Drupal 7.
joe_flash
1
Мне тоже интересно об этих вопросах. Хотелось бы услышать да или нет. Мы активно используем API очереди в одном из наших проектов D7. Визуально я видел, что строки таблицы {queue} очищаются при запуске cron. Так что предположите, что это да.
Шиваджи

Ответы:

19

Когда Drupal запускает задачи cron, он автоматически обрабатывает любую очередь cron, определенную из модулей, в drupal_cron_run(); Сначала hook_cron()вызываются реализации, а затем очереди cron очищаются.

Кроме того hook_cronapi(), вы можете добавить запись для другой функции, обрабатывающей очередь cron вашего модуля.

function mymodule_cronapi($op, $job = NULL) {
  $items = array();

  $items['queue_users_for_synch'] = array(
    'description' => 'Queue all user accounts for synching.',
    'rule' => '0 3 * * *', // Run this job every day at 3am.
    'callback' => 'mymodule_queue_all_users_for_synching',
  );

  $items['clean_queue'] = array(
    'description' => 'Clean the queue for the user synching.',
    'rule' => '0 4 * * *', // Run this job every day at 4 AM.
    'callback' => 'mymodule_clean_queue',
  );

  return $items;
}

function mymodule_clean_queue() {
  $queues = module_invoke('mymodule', 'cron_queue_info');
  drupal_alter('cron_queue_info', $queues);

  // Make sure every queue exists. There is no harm in trying to recreate an
  // existing queue.
  foreach ($queues as $queue_name => $info) {
    DrupalQueue::get($queue_name)->createQueue();
  }

  foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
}

Альтернатива - позволить Drupal обрабатывать очередь cron для вас, но это происходит, когда выполняются задачи cron Drupal. Если вы хотите чаще очищать очередь cron вашего модуля, вы можете добавить только новую задачу cron, обрабатываемую модулем Elysia Cron.

Модуль Elysia Cron обрабатывает очереди cron elysia_cron_run(); эта функция вызывается из elysia_cron_cron()(реализация hook_cron()), drush_elysia_cron_run_wrapper()(обратный вызов команды Drush) и из своего собственного cron.php . Если вы следовали инструкциям в файле INSTALL.txt (в частности, в «ШАГ B: ИЗМЕНИТЬ СИСТЕМУ CRONTAB (ДОПОЛНИТЕЛЬНО)») и заменили любой вызов http://example.com/cron.php на http: // example .com / sites / all / modules / elysia_cron / cron.php , модуль Elysia Cron уже должен обрабатывать очереди cron. Код, который я предложил, можно использовать для ускорения обработки очередей cron, используемых в вашем модуле, если это действительно необходимо.

// This code is part of the code executed from modules/elysia_cron/cron.php.
define('DRUPAL_ROOT', getcwd());

include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_override_server_variables(array(
  'SCRIPT_NAME' => '/cron.php',
));
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
  watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
  drupal_access_denied();
}
elseif (variable_get('maintenance_mode', 0)) {
  watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
  drupal_access_denied();
}
else {
  if (function_exists('elysia_cron_run')) {
    elysia_cron_run();
  }
  else {
    drupal_cron_run();
  }
}
киамалуно
источник
Ах, спасибо @kiam! Это то, что я подозревал, но я не мог обернуть это вокруг себя.
joe_flash
На самом деле, я думаю, что я что-то здесь упускаю. Вы сказали, что альтернатива позволяет Drupal обрабатывать очередь cron для меня; Я предполагаю, что часть моего первоначального вопроса - когда это действительно происходит ? Каждый раз запросы crontab cron.php? Если это так, то это происходит каждую минуту (см. Мой первый комментарий к ответу @ David).
joe_flash
1
Стоит отметить, что у Elysia cron есть собственная реализация процессора cron_queue, в elysia_cron_runкоторой очереди cron автоматически обрабатываются каждый раз, когда запрашивается elysia cron.php.
Дэвид Томас
@joe_flash Я прошу прощения: я должен был быть более ясным. Да, Drupal запускает задачи cron при вызове cron.php (для каждой версии Drupal до Drupal 7). В Drupal 8 cron.php больше не присутствует, а задачи cron выполняются с использованием другого системного пути.
kiamlaluno
2

Очередь будет заполняться через крючок Elysia cronapi в установленное время.

Однако очередь будет обрабатываться всякий раз, когда происходит стандартный запуск Cron Drupal.

Смотрите этот рабочий фрагмент обработки обратного вызова в конце ядра: drupal_cron_run

 foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
Дэвид Томас
источник
Дэвид, может быть, Элизия вносит здесь некоторые сложности? Я установил crontab для запуска cron.phpсценария Elysia каждую минуту , что позволяет Elysia контролировать время выполнения задач с поминутным разрешением. Хотя на самом деле ни одна задача не выполняется каждую минуту - что заставило меня думать, что мне нужно создать задачу, специально предназначенную для работы с очередями?
joe_flash
@joe_flash, пока drupal_cron_runвызывается, ваш обратный вызов работника очереди cron будет обработан.
Дэвид Томас
Ах, я думаю, что ты прав. Однако drupal_cron_runне вызывается из cron.phpскрипта Elysia (когда Elysia включена); elysia_cron_runиспользуется вместо
joe_flash
В этом случае, похоже, что вы не можете использовать hook_cron_queue_infoс Elysia cron, если вы не укажете свой собственный обратный вызов работника, как показано в drupal_cron_runфрагменте основной функции выше.
Дэвид Томас
elysia_cron_runне называем drupal_cron_run, но это делает вызов module_invoke_all('cron_queue_info')и делает некоторые модные штаны многоканальную обработки , что марка дыма выходит мои уши.
joe_flash
1

как указано выше при использовании Elysia Cron, ваши очереди не обрабатываются.

у вас (и у drupal) нет доступа к очередям, которые иначе запускались бы на drupal_run_cron

Обходной путь - создать пользовательскую задачу cron - (это будет видно для elysia cron), чтобы либо обработать все очереди, либо ту, которую вы хотите, и вызвать обработку очереди там. то есть:

function mymodule_cron() {
// below is copied from drupal_cron_run() in common.inc

// Grab the defined cron queues.
$queues = module_invoke_all('cron_queue_info');
drupal_alter('cron_queue_info', $queues);

//if you want to target only one queue you can remove 'foreach'
and your $info = $queues['your_queue'];

  foreach ($queues as $queue_name => $info) {
    if (!empty($info['skip on cron'])) {
      // Do not run if queue wants to skip.
      continue;
    }
    $callback = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
     while (time() < $end && ($item = $queue->claimItem())) {
      try {
        call_user_func($callback, $item->data);
        $queue->deleteItem($item);
      }
      catch (Exception $e) {
        // In case of exception log it and leave the item in the queue
        // to be processed again later.
        watchdog_exception('cron', $e);
      }
    }
  }
}

теперь обработка очередей может контролироваться ElysiaCron

masterperoo
источник
0

Я не использую Elysia, но мое решение всегда было примерно таким:

function mymodule_cron() {
  $queue = DrupalQueue::get('mymoudule_queue');
  $queue->createQueue();
  $item = $queue->claimItem(300);

  if (!empty($item->data)) {

    // Do the work.

    if ($sucess) {
      $queue->deleteItem($item);
      watchdog('mymodule', 'It worked.');
    }
    else {
      watchdog('mymodule', 'It did not work!', array(), WATCHDOG_ALERT);
    }
  }
}

Он обрабатывает только один предмет, для каждого запуска cron. Может быть, вы хотите изменить это.

Мартин Поулсен
источник
0

Я также пытался обдумать это, поскольку впервые использую API очереди вместе с Elysia cron. При ближайшем рассмотрении вы увидите, что Elysia cron выполняет элементы очереди при вызове функции elysia_cron_run . Смотрите этот фрагмент строки 1044 в файле elysia_cron.module :

if (EC_DRUPAL_VERSION >= 7) {
  // D7 Queue processing
  foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
}

Это помогло демистифицировать обработку очередей для меня при использовании Elysia cron.

Алекс Кирстен
источник