Benötige ich einen Cron-Task zum Verarbeiten einer Warteschlange?

32

Ich habe eine Aufgabe, die ungefähr 45 Minuten dauert und jeden Tag ausgeführt werden muss (Synchronisierung von Benutzern mit mehreren externen Datenbanken usw.).

Um die Arbeit zu erledigen, habe ich eine Cron-Warteschlange hook_cron_queue_info()wie folgt eingerichtet:

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

Ich fülle die Warteschlange mit dieser Funktion:

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

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

Die Funktion zum Füllen der Warteschlange wird als Cron-Task aufgerufen. Ich benutze Elysia Cron , daher ist meine Implementierung von 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;
}

Die Worker-Funktion für jedes Warteschlangenelement, das in definiert mymodule_cron_queue_infoist, sieht folgendermaßen aus:

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

Meine Frage ist, wann wird cron tatsächlich mit der Bearbeitung der Warteschlange beginnen?

Angenommen, ich fülle die Warteschlange jeden Tag um 3 Uhr morgens und möchte sie alle 30 Minuten für 120 Sekunden verarbeiten, bis sie fertig ist. Muss ich eine weitere Cron-Aufgabe erstellen?

joe_flash
quelle
Ich sollte erwähnen, dass ich Drupal 7
benutze
1
Ich bin auch neugierig auf diese Fragen. Möchte ja oder nein hören. In einem unserer D7-Projekte wird die Warteschlangen-API häufig verwendet. Visuell habe ich gesehen, dass {queue} Tabellenzeilen gelöscht werden, wenn der Cron ausgeführt wird. Also nimm an, es ist ja.
Sivaji

Antworten:

19

Wenn Drupal Cron-Tasks ausführt, werden automatisch alle aus Modulen definierten Cron-Warteschlangen verarbeitet drupal_cron_run(). Es werden zunächst hook_cron()Implementierungen aufgerufen und anschließend die Cron-Warteschlangen geleert.

Inmplementing hook_cronapi()können Sie einen Eintrag für eine andere Funktion hinzufügen, die die Cron-Warteschlange Ihres Moduls verwaltet.

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

Die Alternative besteht darin, dass Drupal die Cron-Warteschlange für Sie verwaltet. Dies geschieht jedoch, wenn Drupal-Cron-Tasks ausgeführt werden. Wenn Sie die Cron-Warteschlange Ihres Moduls häufiger leeren möchten, können Sie nur eine neue Cron-Aufgabe hinzufügen, die vom Elysia Cron-Modul verwaltet wird.

Das Elysia Cron-Modul verwaltet die Cron-Warteschlangen in elysia_cron_run(); Diese Funktion wird von elysia_cron_cron()(einer Implementierung von hook_cron()), drush_elysia_cron_run_wrapper()(einem Drush-Befehlsrückruf) und von seiner eigenen cron.php aufgerufen . Wenn Sie den Anweisungen in der Datei INSTALL.txt gefolgt sind (insbesondere in "SCHRITT B: SYSTEM CRONTAB ÄNDERN (OPTIONAL)") und einen Aufruf von http://example.com/cron.php durch http: // example ersetzt haben .com / sites / all / modules / elysia_cron / cron.php sollte die Elysia cron - Modul bereits sein , die cron - Warteschlangen Handhabung. Der von mir vorgeschlagene Code kann verwendet werden, um die Verarbeitung der von Ihrem Modul verwendeten Cron-Warteschlangen zu beschleunigen, wenn dies tatsächlich erforderlich ist.

// 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();
  }
}
kiamlaluno
quelle
Ah, danke @kiam! Das habe ich vermutet, aber ich konnte mein Hirn nicht ganz darum wickeln.
Joe_flash
Eigentlich vermisse ich hier etwas. Sie sagten, die Alternative ließe Drupal die Cron-Warteschlange für mich erledigen. Ich denke, ein Teil meiner ursprünglichen Frage ist, wann das tatsächlich passiert . Jedes mal crontab anfragen cron.php? Wenn das der Fall ist, passiert das jede Minute (siehe meinen ersten Kommentar zu @ Davids Antwort).
Joe_flash
1
Erwähnenswert ist, dass Elysia cron anscheinend über eine eigene cron_queue-Prozessorimplementierung verfügt, elysia_cron_runbei der die cron-Warteschlangen jedes Mal automatisch verarbeitet werden, wenn Elysias cron.php angefordert wird.
David Thomas
@ Joe_flash Es tut mir leid: Ich hätte klarer sein sollen. Ja, Drupal führt cron-Tasks aus, wenn cron.php aufgerufen wird (für jede Drupal-Version bis Drupal 7). In Drupal 8 ist cron.php nicht mehr vorhanden und cron-Tasks werden unter einem anderen Systempfad ausgeführt.
Kiamlaluno
2

Die Warteschlange wird zum festgelegten Zeitpunkt über den Elysia-Cronapi-Hook gefüllt.

Die Warteschlange wird jedoch immer dann verarbeitet, wenn der Standard-Drupal-Cron-Lauf stattfindet.

Sehen Sie sich dieses Worker-Callback-Verarbeitungs-Snippet am Ende des Kerns an: 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 Thomas
quelle
David, vielleicht führt Elysia hier ein bisschen Komplikation ein? Ich habe crontab so eingestellt, dass das Elysia- cron.phpSkript jede Minute ausgelöst wird , wodurch Elysia die Task-Zeiten mit einer minutengenauen Auflösung steuern kann. Tatsächlich läuft aber keine Aufgabe jede Minute - was hat mich zu der Annahme veranlasst, dass ich eine Aufgabe erstellen musste, um speziell an Warteschlangen zu arbeiten?
Joe_flash
@joe_flash Solange drupal_cron_runaufgerufen wird, wird Ihr Cron Queue Worker Callback verarbeitet.
David Thomas
Ah, ich denke du hast recht. Wird drupal_cron_runjedoch nicht vom Elysia- cron.phpSkript aufgerufen (wenn Elysia aktiviert ist); elysia_cron_runwird stattdessen verwendet.
Joe_flash
In diesem Fall können Sie hook_cron_queue_infoElysia cron anscheinend nicht verwenden , es sei denn, Sie geben Ihren eigenen Worker-Rückruf an, wie im drupal_cron_runobigen Kernfunktionsausschnitt beschrieben.
David Thomas
elysia_cron_runnicht nennen drupal_cron_run, aber es tut Anruf module_invoke_all('cron_queue_info')und hat einige fancy-Hose Multi-Channel - Handling , das macht meine Ohren rauchen kommen.
Joe_flash
1

Wie oben erwähnt, werden Ihre Warteschlangen bei der Verwendung von Elysia Cron nicht verarbeitet.

Sie (und Drupal) haben keinen Zugriff auf Warteschlangen, die andernfalls auf Drupal_run_cron ausgeführt würden

Die Problemumgehung besteht darin, eine benutzerdefinierte Cron-Aufgabe zu erstellen (die für elysia cron sichtbar ist), um entweder alle oder eine gewünschte Warteschlange zu verarbeiten und die Verarbeitung der Warteschlange dort aufzurufen. dh:

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

Jetzt kann die Verarbeitung von Warteschlangen von ElysiaCron gesteuert werden

Masterperoo
quelle
0

Ich verwende Elysia nicht, aber meine Lösung war schon immer so:

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

Es wird nur ein Element für jeden Cron-Lauf verarbeitet. Vielleicht möchten Sie das ändern.

Martin Poulsen
quelle
0

Ich habe auch versucht, mich damit abzufinden, da ich zusammen mit Elysia cron zum ersten Mal die Queue-API verwende. Bei näherer Betrachtung können Sie sehen, dass Elysia cron beim Aufruf der Funktion elysia_cron_run Queue Items ausführt . Sehen Sie sich diesen Ausschnitt aus Zeile 1044 in der Datei elysia_cron.module an :

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

Dies hat mir bei der Verwendung von Elysia cron geholfen, die Warteschlangenverarbeitung zu entmystifizieren.

Alex Kirsten
quelle