Чи потрібно мені завдання cron для обробки черги?


32

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

Щоб впоратися з роботою, я встановив чергу кронів із 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 хвилин, поки це не закінчиться-- чи потрібно мені створити ще одне завдання з крона ?


Я мушу зазначити, що я використовую Drupal 7.
joe_flash

1
Мені також цікаво з цього питання. Хотіли б почути так чи ні. Ми активно використовуємо API черги в одному з наших проектів D7. Візуально я бачив, що рядки таблиці {queue} очищаються під час запуску крона. Тож припустимо, що так.
Сіваджі

Відповіді:


19

Коли Drupal виконує завдання cron, він автоматично обробляє будь-яку чергу черг, визначену з модулів, в drupal_cron_run(); спочатку hook_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 керувати чергою для вас, але це відбувається, коли виконуються завдання Drupal 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 / module / elysia_cron / cron.php , модуль Elysia 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

Власне, я думаю, що мені чогось тут не вистачає. Ви сказали, що альтернатива - дозволити Друпалу керувати чергою для мене; Я припускаю, що частина мого оригінального запитання - коли це насправді відбувається ? Кожен раз, коли запити на Crontab cron.php? Якщо це так, це відбувається щохвилини (див. Мій перший коментар до відповіді @ Девіда).
joe_flash

1
Варто зазначити, що, здається, у Elysia cron є власна реалізація процесора cron_queue, в elysia_cron_runякій черги cron автоматично обробляються щоразу, коли запит на cron.php Elysia.
Девід Томас

@joe_flash Мені шкода: я повинен був бути зрозумілішим. Так, Drupal виконує завдання cron, коли викликається cron.php (для кожної версії Drupal до Drupal 7). У Drupal 8 cron.php більше немає, а завдання cron виконуються за допомогою іншого системного шляху.
kiamlaluno

2

Черга буде заповнена через гачок Elysia cronapi у встановлений час.

Однак черга буде оброблятися кожного разу, коли трапиться стандартний запуск Drupal cron.

Дивіться фрагмент обробки зворотного виклику робочого в кінці ядра: 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поки вас викликають, обробка зворотного дзвінка вашої черги буде оброблена.
Девід Томас

Ах, я думаю, ти маєш рацію. Однак 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


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);
    }
  }
}

Він обробляє лише один предмет для кожного запуску крона. Можливо, ви хочете це змінити.


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.