WP Cron не выполняется по истечении времени

27 мар. 2013 г., 17:12:16
Просмотры: 22K
Голосов: 19

Цель

Я хочу использовать wp_schedule_single_event() для выполнения одиночного события, которое отправляет мне электронное письмо через 8 минут после отправки пользователем формы.

Проблема

Следующий код находится в моем functions.php:

function nkapi_send_to_system( $args ) {
  wp_mail( 'xxx', 'xxx', $args );
}

add_action( 'nkapi_send', 'nkapi_send_to_system' );

function schedule_event( $id ) {
  wp_schedule_single_event( current_time( 'timestamp' ) + 480, 'nkapi_send', array( $id ) );
}

И следующий код используется для вызова schedule-event:

schedule_event( $_SESSION['insert_id'] ); // переменная $_SESSION содержит число INT

После ожидания более 8 минут в моем почтовом ящике не было письма.

Что я пробовал

С помощью плагина Core Control можно увидеть, какие cron-задачи запланированы.

Экран Core Control

После нескольких изменений мне удалось настроить их довольно правильно, и что еще лучше, когда я нажимаю "Run Now", я действительно получаю письмо в свой почтовый ящик.

Но почему cron-задачи не выполняются, когда я посещаю свой сайт через 8 минут. Что может быть не так с этим кодом? Должен сказать, что это мой первый опыт использования WP Cron.

Я попробовал еще

После комментария vancoder я решил проверить, работает ли код, если поместить следующий код непосредственно в functions.php:

function schedule_event( $id ) {
  wp_schedule_single_event( time(), 'nkapi_send', array( $id ) );
}

if ( isset( $_SESSION['insert_id'] ) ) {
  if ( ! array_key_exists( 'insert_scheduled', $_SESSION ) || $_SESSION['insert_scheduled'] != $_SESSION['insert_id'] ) {
    schedule_event( $_SESSION['insert_id'] );
    $_SESSION['insert_scheduled'] = $_SESSION['insert_id'];
  }
}

Недостаток этого кода в том, что пользователь должен перейти на другую страницу, прежде чем этот код будет выполнен. Но с другой стороны, это тоже не работает, так что это не была бы моя первая проблема...

5
Комментарии

Где и как вызывается schedule_event( $_SESSION['insert_id'] );?

vancoder vancoder
27 мар. 2013 г. 18:23:31

Шорткод включает отдельный файл (с формой) на странице, при отправке формы страница перезагружается, и тот же файл затем выполняет schedule_event( ), допустим, в начале включаемого файла, загружаемого через шорткод.

Mike Madern Mike Madern
28 мар. 2013 г. 09:48:50

Работают ли вообще какие-либо крон-задачи? wp_version_check и другие?

vancoder vancoder
28 мар. 2013 г. 19:28:37

У меня не работает ни один из крон-заданий. В чём может быть проблема?

Mike Madern Mike Madern
29 мар. 2013 г. 10:09:15

Чтобы уточнить - даже системные cron-задачи не выполняются?

vancoder vancoder
4 апр. 2013 г. 19:44:42
Все ответы на вопрос 6
8
18

Сначала определите свои пользовательские расписания для cron-заданий.

add_filter('cron_schedules', array($this, 'cron_schedules'));

public function cron_schedules($schedules){
    $prefix = 'cron_';// Избегаем конфликтов с другими cron-заданиями. Пример: cron_30_mins
    $schedule_options = array(
        '30_mins' => array(
            'display' => '30 минут',
            'interval' => '1800'
        ),
        '1_hours' => array(
            'display' => 'Час',
            'interval' => '3600'
        ),
        '2_hours' => array(
            'display' => '2 часа',
            'interval' => '7200'
        )
    );
    /* Добавляем каждое пользовательское расписание в систему cron. */
    foreach($schedule_options as $schedule_key => $schedule){
        $schedules[$prefix.$schedule_key] = array(
            'interval' => $schedule['interval'],
            'display' => __('Каждые '.$schedule['display'])
        );
     }
     return $schedules;
}

Вам нужно решить, где и когда запланировать событие.

Вот пример кода, который вызывает пользовательский метод класса:

$schedule = $this->schedule_task(array(
    'timestamp' => current_time('timestamp'), // Определяем, когда запланировать задачу.
    'recurrence' => 'cron_30_mins',// Выбираем одно из ранее заданных расписаний.
    'hook' => 'custom_imap_import'// Устанавливаем имя вашей cron-задачи.
));

Вот код, который непосредственно планирует событие:

private function schedule_task($task){
    /* Должна быть информация о задаче. */
    if(!$task){
        return false;
    }
    /* Устанавливаем список обязательных ключей задачи. */
    $required_keys = array(
        'timestamp',
        'recurrence',
        'hook'
    );
    /* Проверяем наличие необходимой информации. */
    $missing_keys = array();
    foreach($required_keys as $key){
        if(!array_key_exists($key, $task)){
            $missing_keys[] = $key;
        }
    }
    /* Проверяем отсутствующие ключи. */
    if(!empty($missing_keys)){
        return false;
    }
    /* Задача не должна быть уже запланирована. */
    if(wp_next_scheduled($task['hook'])){
        wp_clear_scheduled_hook($task['hook']);
    }
    /* Планируем выполнение задачи. */
    wp_schedule_event($task['timestamp'], $task['recurrence'], $task['hook']);
    return true;
}

Теперь вам остается только вызвать имя вашей пользовательской cron-задачи. В этом примере имя задачи — custom_imap_import.

add_action('custom_imap_import', array($this, 'do_imap_import'));

public function do_imap_import(){
    // .... Выполняем действия при срабатывании cron ....
}

Таким образом, в этом примере $this->do_imap_import(); будет вызываться каждые 30 минут (при условии достаточного трафика на вашем сайте).


Примечания

Требуется посещение страницы, чтобы cron сработал в нужное время.

Пример: Если вы запланировали задачу с интервалом в 30 минут, но на ваш сайт никто не заходит в течение 4 часов, cron-задача не сработает, пока посетитель не зайдет на сайт через 4 часа. Если вам действительно нужно, чтобы задача выполнялась каждые 30 минут, рекомендуется настроить настоящий cron-запуск через хостинг-провайдера для посещения сайта с нужным интервалом.

Cron-задачи WordPress не замедляют ваш сайт!

Возможно, вы думаете: что если cron-скрипт выполняется долго, придется ли посетителям ждать его завершения? Нет! Как это возможно? Если посмотреть файл wp-cron.php, вы найдете строку:

ignore_user_abort(true);

Это настройка php.ini, которая означает, что если загрузка сайта/скрипта прервется, сам скрипт продолжит выполняться.

Если посмотреть файл wp-includes/cron.php, вы найдете строку:

wp_remote_post( $cron_url, 
array('timeout' => 0.01,
 'blocking' => false, 
 'sslverify' => apply_filters('https_local_ssl_verify', true)) );

Это означает, что WordPress будет ждать только 0.01 секунды для запуска выполнения, затем прервет соединение, но так как ignore_user_abort установлен в true, скрипт продолжит выполняться. Эта функциональность — огромное преимущество для выполнения больших скриптов в cron-задачах WordPress.

Функции, которые могут помочь:

4 апр. 2013 г. 17:22:45
Комментарии

Это очень подробный ответ, который, насколько я могу судить, не затрагивает главный вопрос — почему все запланированные задачи (включая системные) завершаются с ошибкой.

vancoder vancoder
4 апр. 2013 г. 19:40:42

Этот ответ направлен на то, чтобы направить пользователя в правильном направлении для понимания и корректной настройки запланированных задач (cron) в WordPress.

Michael Ecklund Michael Ecklund
4 апр. 2013 г. 20:07:54

Это действительно помогло мне лучше разобраться в работе планировщика задач (cron) в WordPress.

Mike Madern Mike Madern
9 апр. 2013 г. 16:08:17

Майкл, тебе стоит отвечать чаще. Отличный ответ +1

kaiser kaiser
3 окт. 2013 г. 05:36:35

В какой момент следует выполнять "add_action('custom_imap_import', array($this, 'do_imap_import'))", если речь идет о классе плагина? В конструкторе?

codecowboy codecowboy
11 окт. 2013 г. 20:25:13

@codecowboy Да, или в том месте, где он может быть загружен, когда cron будет готов к выполнению.

Michael Ecklund Michael Ecklund
21 окт. 2013 г. 23:12:25

Ядро уже предоставляет интервал hourly, поэтому вам не нужно использовать 1_hours.

Ian Dunn Ian Dunn
25 нояб. 2014 г. 03:06:29

Думаю, WP_Cron использует GMT "под капотом", как и остальная часть WP, поэтому лучше запланировать первое событие на time() вместо current_time().

Ian Dunn Ian Dunn
25 нояб. 2014 г. 03:09:08
Показать остальные 3 комментариев
2

Сначала, пожалуйста, подтвердите, что у вас не включены плагины кэширования? Плагины кэширования могут мешать работе cron-задач, так как ваши посетители получают не живую страницу, а её закэшированную версию.

Если у вас включён плагин кэширования, вы можете выбрать одну из ваших страниц, добавить исключение в настройках плагина кэширования для этой страницы, чтобы она никогда не кэшировалась.

Затем вам нужно будет вручную создать cron-задачу (используя cPanel, если вы находитесь на shared-хостинге, или через терминал, если это VPS/выделенный сервер), которая будет посещать эту страницу каждые несколько минут.

Надеюсь, это поможет!

5 апр. 2013 г. 17:59:13
Комментарии

У меня включен плагин кэширования! Если точнее - W3 Total Cache

Mike Madern Mike Madern
9 апр. 2013 г. 16:04:55

Есть опции для отключения всего кэша, но я не вижу возможности включать/отключать кэширование только для плагинов

user1019042 user1019042
2 нояб. 2020 г. 21:53:06
1

WordPress Cron позволяет планировать задачи, но они выполняются только при наличии запроса к сайту. При каждом полученном запросе WordPress проверяет, есть ли задания cron для обработки, и если да, асинхронно отправляет запрос к /wp-cron.php?doing_wp_cron для выполнения задачи. Если запрос не поступает в запланированное время, процесс cron не запускается.

Поскольку вы можете просматривать и запускать запланированные задания, возможно, что нет запросов, которые инициируют запуск cron, особенно если вы используете плагин кеширования. Лучший вариант для переноса этого на более регулярное расписание — отключить стандартную проверку в WordPress и использовать crontab.

Сначала, чтобы отключить стандартную проверку (что может немного улучшить производительность на стороне клиента), добавьте следующее в wp-config.php:

// Отключить стандартную проверку заданий cron WordPress при загрузке страниц
define( 'DISABLE_WP_CRON', true );

Затем создайте задачу для запроса страницы wp-cron.php раз в минуту для обработки всех заданий на серверной стороне. В командной строке введите crontab -e и добавьте строку, которая выглядит следующим образом:

*/1 * * * * /usr/bin/curl --silent http://example.com/wp-cron.php?doing_wp_cron=$(date +\%s.\%N) >/dev/null 
6 апр. 2013 г. 03:43:17
Комментарии

Если не задать значение для doing_wp_cron, это с большой вероятностью может привести к двойному выполнению задач. Используйте doing_wp_cron=$(date +\%s.\%N), чтобы избежать этого.

Alexander Garden Alexander Garden
10 нояб. 2013 г. 13:04:14
0

Для тех, кто защищает свой (разрабатываемый) сайт от публичного доступа, HTTP-аутентификация может стать причиной неработоспособности WP Cron.

На случай, если это кому-то поможет, вот список действий, которые я выполнил перед тем, как выявил и понял требования WP Cron:

  • Я заметил, что события корректно планировались и могли быть запущены с помощью WP-CLI.
  • Также заметил, что доступ к /wp-cron.php?doing_wp_cron через браузер действительно запускал выполнение, как упоминалось, например, в 13625.
  • Была прочитана официальная документация.
  • Я убедился, что DISABLE_WP_CRON не установлена.
  • И узнал, что ALTERNATE_WP_CRON является рабочим, но неудовлетворительным решением.
  • Чтобы исключить регрессионные ошибки, я попробовал установить несколько старых версий WordPress.
  • Все плагины были отключены, а тема изменена на стандартную, чтобы изолировать проблему.
  • Люди упоминали термин loopbacks, но из-за отсутствия связанных ошибок или предупреждений я посчитал это несущественным.
  • В конце концов, была найдена вопрос с ответом, который объяснял, как отладить cron.
  • После подключения Xdebug к wp_cron() я действительно увидел, что выполнение завершалось в wp_remote_post(), что явно не сработало.

Когда я понял, что искать, я нашел эту отличную статью о причинах проблем с WP Cron от Джеффа Старра. В конце статьи он ссылается на свой плагин wp-cron-http-auth.

17 июн. 2020 г. 10:37:19
1

Убедитесь, что DISABLE_WP_CRON не установлен в вашем конфигурационном файле.

Если это не помогает, попробуйте отключить все плагины (кроме core control - хотя я бы рекомендовал использовать wp-crontrol) и проверьте, работают ли основные задачи. Если они работают, значит, у вас где-то есть конфликт с плагинами.

Аналогично, попробуйте переключиться на стандартную тему типа twentysomething.

Если ни один из этих вариантов не помогает, скорее всего, проблема на стороне хостинга.

4 апр. 2013 г. 20:19:23
Комментарии

У меня нет параметра DISABLE_WP_CRON в файле wp-config.php, попробую ещё несколько вариантов и вернусь позже

Mike Madern Mike Madern
9 апр. 2013 г. 16:07:21
0

Проверьте любой плагин, который скрывает WordPress.

Как понять, что проблема в этом?

  1. Перейдите по адресу http(s)://вашсайт.com/wp-cron.php Вы должны увидеть пустую страницу. Абсолютно пустую.
  2. Кроме того, в менеджере cron-задач вы должны увидеть время в поле "Следующее выполнение": Cron задача запланирована - если wp-cron.php работает правильно (не просто текст "В очереди" - а конкретное время - для некоторых записей "В очереди" иногда нормально, но если это единственное, что вы видите -> ваш cron не работает.)

+1. Не доверяйте плагинам, которые "проверяют работу cron" - например, плагин WP Cron status checker показывал, что cron работает. Но на самом деле это было не так. Что бы он ни показывал - верьте своим глазам, а не этому плагину!

Вывод: Если вы получаете ошибку 404 - отключите a) не только кэширующие плагины, как советуют другие b) но и любые плагины, скрывающие WordPress.

16 апр. 2019 г. 00:45:30