web-dev-qa-db-fra.com

Ai-je besoin d'une tâche cron pour traiter une file d'attente?

J'ai une tâche qui prend environ 45 minutes et doit être effectuée tous les jours (synchronisation des utilisateurs vers plusieurs bases de données externes, etc.).

Pour gérer le travail, j'ai mis en place une file d'attente cron avec hook_cron_queue_info() comme suit:

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

Je remplis la file d'attente en utilisant cette fonction:

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

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

La fonction de remplissage de file d'attente est appelée en tant que tâche cron. J'utilise Elysia Cron , donc mon implémentation de hook_cronapi() est:

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

La fonction de travail pour chaque élément de file d'attente, définie dans mymodule_cron_queue_info Est comme:

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

Ma question est, quand cron va-t-il réellement commencer le traitement de la file d'attente?

Supposons que je remplisse la file d'attente tous les jours à 3 heures du matin et que je souhaite la traiter 120 secondes toutes les 30 minutes jusqu'à ce qu'elle soit terminée - dois-je créer une autre tâche cron?

32
joe_flash

Lorsque Drupal exécute des tâches cron, il gère automatiquement toutes les files d'attente cron définies à partir des modules, dans drupal_cron_run() ; premières hook_cron() implémentations sont invoqués, puis les files d'attente cron sont vidées.

En implémentant hook_cronapi(), vous pouvez ajouter une entrée pour une autre fonction gérant la file d'attente cron de votre module.

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

L'alternative consiste à laisser Drupal gérer la file d'attente cron pour vous, mais cela se produit lorsque Drupal tâches cron sont exécutées. Si vous souhaitez vider la file d'attente cron de votre plus fréquemment, vous ne pouvez ajouter qu'une nouvelle tâche cron gérée par le module Elysia Cron.

Le module Elysia Cron gère les files d'attente cron dans elysia_cron_run() ; cette fonction est invoquée depuis elysia_cron_cron() (une implémentation de hook_cron()), drush_elysia_cron_run_wrapper() (un rappel de commande Drush), et depuis son propre cron.php . Si vous avez suivi les instructions du fichier INSTALL.txt (en particulier dans "ÉTAPE B: CHANGER LE SYSTÈME CRONTAB (FACULTATIF)"), et remplacé toute invocation de http: // exemple. com/cron.php avec http://example.com/sites/all/modules/elysia_cron/cron.php , le module Elysia Cron devrait déjà gérer les files d'attente cron. Le code que j'ai suggéré pourrait être utilisé pour accélérer la gestion des files d'attente cron utilisées à partir de votre module, si cela est effectivement nécessaire.

// 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();
  }
}
20
kiamlaluno

La file d'attente sera remplie via le crochet Elysia cronapi à l'heure définie.

Cependant, la file d'attente sera traitée chaque fois que l'exécution standard Drupal cron se produit.

Voir cet extrait de traitement de rappel de travailleur à la fin du noyau: 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);
    }
  }
2
David Thomas

comme indiqué ci-dessus lorsque vous utilisez Elysia Cron, vos files d'attente ne sont pas traitées.

vous (et drupal) n'avez pas accès aux files d'attente qui s'exécuteraient autrement sur drupal_run_cron

la solution de contournement consiste à créer une tâche cron personnalisée - (cela sera visible par elysia cron) pour traiter toutes les files d'attente ou celle que vous voulez et y invoquer le traitement des files d'attente. c'est à dire:

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

maintenant le traitement des files d'attente peut être contrôlé par ElysiaCron

1
masterperoo

Je n'utilise pas Elysia, mais ma solution a toujours été quelque chose comme ça:

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

Il ne gère qu'un seul élément, pour chaque exécution cron. Vous voulez peut-être changer cela.

0
Martin Poulsen

J'ai également essayé de comprendre cela, car j'utilise l'API Queue pour la première fois avec Elysia cron. En y regardant de plus près, vous pouvez voir qu'Elysia cron exécute les éléments de la file d'attente lorsque la fonction elysia_cron_run est appelée. Voir cet extrait de ligne 1044 dans le fichier 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);
    }
  }
}

Cela m'a aidé à démystifier le traitement de la file d'attente pour moi lors de l'utilisation d'Elysia cron.

0
Alex Kirsten