Czy potrzebuję zadania CRON do przetwarzania kolejki?

32

Mam zadanie, które zajmuje około 45 minut i musi być wykonywane codziennie (synchronizacja użytkowników z kilkoma zewnętrznymi bazami danych itp.).

Aby obsłużyć pracę, skonfigurowałem kolejkę cron hook_cron_queue_info()w następujący sposób:

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

Wypełniam kolejkę za pomocą tej funkcji:

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

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

Funkcja wypełniania kolejek jest wywoływana jako zadanie cron. Używam Elysia Cron , więc moja implementacja hook_cronapi()to:

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

Funkcja robocza dla każdego zdefiniowanego elementu kolejki mymodule_cron_queue_infojest następująca:

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

Moje pytanie brzmi: kiedy cron faktycznie zacznie przetwarzać kolejkę?

Powiedzmy, że wypełniam kolejkę codziennie o 3 nad ranem i chcę ją przetworzyć 120 sekund na każde 30 minut, aż się skończy - czy muszę utworzyć kolejne zadanie CRON ?

joe_flash
źródło
Powinienem wspomnieć, że używam Drupala 7.
joe_flash
1
Jestem również ciekawa tych pytań. Chciałbym usłyszeć tak lub nie. Często używamy interfejsu API kolejki w jednym z naszych projektów D7. Wizualnie widziałem, że wiersze tabeli {kolejka} są czyszczone po uruchomieniu crona. Załóż więc, że tak.
Sivaji

Odpowiedzi:

19

Kiedy Drupal uruchamia zadania cron, automatycznie obsługuje każdą kolejkę cron zdefiniowaną z modułów, w drupal_cron_run(); pierwsze hook_cron()implementacje są wywoływane, a następnie kolejki cron są opróżniane.

Podczas wdrażania hook_cronapi()możesz dodać wpis dla innej funkcji obsługującej kolejkę cron Twojego modułu.

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

Alternatywą jest zezwolenie Drupalowi na obsługę kolejki cron, ale dzieje się tak, gdy wykonywane są zadania cron Drupal. Jeśli chcesz częściej opróżniać kolejkę cron swojego modułu, możesz dodać tylko nowe zadanie cron obsługiwane przez moduł Elysia Cron.

Moduł Elysia Cron obsługuje kolejki cron elysia_cron_run(); ta funkcja jest wywoływana z elysia_cron_cron()(implementacja hook_cron()), drush_elysia_cron_run_wrapper()(wywołanie zwrotne komendy Drush) oraz z własnego cron.php . Jeżeli postępujesz zgodnie z instrukcjami w INSTALL.txt pliku (w szczególności w „Etap B: Zmiana systemu crontab (opcja)”), i zastąpić dowolny wywołanie http://example.com/cron.php z http: // przykład .com / sites / all / modules / elysia_cron / cron.php , moduł Elysia Cron powinien już obsługiwać kolejki cron. Kod, który zasugerowałem, może zostać wykorzystany do przyspieszenia obsługi kolejek cron używanych z twojego modułu, jeśli istnieje taka potrzeba.

// 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
źródło
Ach, dzięki @kiam! Podejrzewałem to, ale nie mogłem całkowicie otulić się mózgiem.
joe_flash
Właściwie myślę, że coś tu brakuje. Powiedziałeś, że alternatywą jest pozwolenie Drupalowi zająć się kolejką cron dla mnie; Wydaje mi się, że część mojego pierwotnego pytania brzmi, kiedy to się dzieje ? Za każdym razem, gdy żąda crontab cron.php? Jeśli tak jest, dzieje się to co minutę (patrz mój pierwszy komentarz do odpowiedzi @ Davida).
joe_flash
1
Warto wspomnieć, że wydaje się, że Elysia cron ma własną implementację procesora cron_queue, elysia_cron_runa kolejki cron są automatycznie przetwarzane za każdym razem, gdy żądane jest cron.php Elysii.
David Thomas
@joe_flash Przykro mi: Powinienem był być bardziej zrozumiały. Tak, Drupal uruchamia zadania cron po wywołaniu cron.php (dla każdej wersji Drupala do Drupal 7). W Drupal 8 plik cron.php nie jest już obecny, a zadania cron są wykonywane przy użyciu innej ścieżki systemowej.
kiamlaluno
2

Kolejka zostanie zapełniona przez hak Elysia cronapi o ustalonej godzinie.

Jednak kolejka będzie przetwarzana za każdym razem, gdy nastąpi standardowe uruchomienie cron Drupal.

Zobacz ten fragment kodu przetwarzania wywołania zwrotnego pracownika na końcu rdzenia: 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
źródło
David, może Elysia wprowadza tutaj trochę komplikacji? Ustawiłem crontab, aby uruchamiał cron.phpskrypt Elysia co minutę , co pozwala Elysii kontrolować czasy zadań z dokładnością co do minuty. Żadne zadanie nie jest jednak uruchamiane co minutę - co sprawiło, że pomyślałem, że muszę utworzyć zadanie do pracy z kolejkami?
joe_flash,
@joe_flash tak długo, jak drupal_cron_runjest wywoływany, wywołanie zwrotne pracownika kolejki cron będzie przetwarzane.
David Thomas
Ach, myślę, że masz rację. Jednak drupal_cron_runnie jest wywoływany ze cron.phpskryptu Elysia (gdy Elysia jest włączona); elysia_cron_runjest używany zamiast tego.
joe_flash
W takim przypadku wygląda na to, że nie możesz używać hook_cron_queue_infoz cronem Elysia, chyba że określisz własne wywołanie zwrotne pracownika, zgodnie z drupal_cron_runpowyższym fragmentem funkcji podstawowej .
David Thomas
elysia_cron_runnie zadzwonić drupal_cron_run, ale nie rozmowy module_invoke_all('cron_queue_info')i nie wymyślnej spodnie obsługi sprawia, że dym wyjdzie moje uszy wielokanałowego.
joe_flash
1

jak wspomniano powyżej podczas korzystania z Elysia Cron, kolejki nie są przetwarzane.

ty (i drupal) nie masz dostępu do kolejek, które w innym przypadku działałyby na drupal_run_cron

obejście polega na utworzeniu niestandardowego zadania cron - (będzie to widoczne dla elysia cron) w celu przetworzenia wszystkich kolejek lub jednego z nich i wywołania tam przetwarzania kolejek. to znaczy:

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

teraz przetwarzanie kolejek może być kontrolowane przez ElysiaCron

masterperoo
źródło
0

Nie używam Elysii, ale moje rozwiązanie zawsze było takie:

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

Obsługuje tylko jeden przedmiot dla każdego przebiegu cron. Może chcesz to zmienić.

Martin Poulsen
źródło
0

Próbowałem też to obejść, ponieważ po raz pierwszy korzystam z interfejsu API kolejki wraz z cronem Elysia. Po bliższym przyjrzeniu się widać, że cron Elysia wykonuje pozycje w kolejce, gdy wywoływana jest funkcja elysia_cron_run . Zobacz ten fragment kodu z linii 1044 w pliku 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);
    }
  }
}

Pomogło mi to w odkrywaniu przetwarzania kolejek podczas korzystania z cronu Elysia.

Alex Kirsten
źródło