Ai-je besoin d'une tâche périodique pour traiter une file d'attente?


32

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

Pour gérer le travail, j'ai configuré une file d'attente cron avec hook_cron_queue_info()les éléments suivants:

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 la file d'attente est appelée en tant que tâche périodique. J'utilise Elysia Cron , mon implémentation hook_cronapi()est donc:

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 worker pour chaque élément de la file d'attente, définie dans, mymodule_cron_queue_infoest la suivante:

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

Ma question est la suivante: quand cron commencera-t-il réellement à traiter la file d'attente?

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


Je dois mentionner que j'utilise Drupal 7.
joe_flash

1
Je suis également curieux de ces questions. Voudrais entendre oui ou non. Nous utilisons fortement les API de file d'attente dans l'un de nos projets D7. Visuellement, j'ai vu que les lignes de la table {queue} sont effacées lors de l'exécution du cron. Alors présume que c'est oui.
Sivaji

Réponses:


19

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

En implémentation 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 est de laisser Drupal gérer la file d'attente cron pour vous, mais cela se produit lorsque les tâches cron Drupal sont exécutées. Si vous souhaitez vider la file d'attente périodique de votre module plus souvent, vous pouvez uniquement ajouter une nouvelle tâche périodique gérée par le module Elysia Cron.

Le module Elysia Cron gère les files d'attente cron elysia_cron_run(); cette fonction est appelé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 CRONTAB DU SYSTÈME (FACULTATIF)"), et avez remplacé tout appel de http://example.com/cron.php par http: // exemple. .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 le traitement des files d'attente cron utilisées à partir de votre module, si cela était 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();
  }
}

Ah, merci @kiam! C'est ce que je soupçonnais, mais je n'arrivais pas à comprendre.
joe_flash

En fait, je pense que je manque quelque chose ici. Vous avez dit que l’autre solution consistait à laisser Drupal gérer la file d’attente cron pour moi; Je suppose qu'une partie de ma question initiale est de savoir quand cela se produit réellement . Chaque fois que crontab demande cron.php? Si c'est le cas, cela se produit toutes les minutes (voir mon premier commentaire sur la réponse de @ David).
joe_flash

1
Il est à noter qu'il apparaît qu'Elysia cron a sa propre implémentation de processeur cron_queue elysia_cron_runavec des files d'attente cron traitées automatiquement à chaque demande du fichier cron.php d'Elysia.
David Thomas

@ joe_flash Je suis désolé: j'aurais dû être plus clair. Oui, Drupal exécute des tâches périodiques lorsque cron.php est appelé (pour chaque version de Drupal jusqu'à Drupal 7). Dans Drupal 8, cron.php n'est plus présent et les tâches périodiques sont exécutées à l'aide d'un chemin système différent.
kiamlaluno

2

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

Cependant, la file d'attente sera traitée à chaque exécution de l'exécution cron Drupal standard.

Voir l'extrait de traitement de rappel de ce 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);
    }
  }

David, peut-être qu'Elysia introduit un peu de complication ici? J'ai configuré crontab pour déclencher le cron.phpscript Elysia toutes les minutes , ce qui permet à Elysia de contrôler les temps de travail avec une résolution minute. Cependant, aucune tâche ne s'exécute réellement toutes les minutes - qu'est-ce qui m'a fait penser que je devais créer une tâche pour travailler spécifiquement sur les files d'attente?
joe_flash

@joe_flash tant que l' drupal_cron_runappel est en cours, le rappel de votre opérateur de file d'attente à cron sera traité.
David Thomas

Ah, je pense que tu as raison. Cependant, drupal_cron_runne vous appelez pas à partir du cron.phpscript Elysia (lorsque Elysia est activé); elysia_cron_runest utilisé à la place.
joe_flash

Dans ce cas, il semble que vous ne puissiez pas utiliser hook_cron_queue_infoElysia cron, à moins que vous ne spécifiiez votre propre rappel de travailleur, conformément à l' drupal_cron_runextrait de fonction principal ci-dessus.
David Thomas

elysia_cron_runne remet pas drupal_cron_run, mais il fait appel module_invoke_all('cron_queue_info')et fait quelques-pantalons fantaisie manipulation multi-canal qui fait la fumée sortir mes oreilles.
joe_flash

1

Comme indiqué ci-dessus lors de l'utilisation d'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 consiste à créer une tâche périodique personnalisée (cette tâche sera visible par elysia cron) afin de traiter toutes les files d'attente ou une de celles que vous souhaitez et d'appeler le traitement de la file d'attente à cet emplacement. 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);
      }
    }
  }
}

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


0

Je n'utilise pas Elysia, mais ma solution a toujours été la suivante:

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. Peut-être que vous voulez changer cela.


0

J'ai également essayé de comprendre la situation en utilisant pour la première fois l'API Queue avec Elysia Cron. En y regardant de plus près, vous constaterez 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 la 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 lors de l'utilisation d'Elysia cron.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.