Cómo ejecutar WP-Cron correctamente en redes Multisite de WordPress
Tengo una instalación de WordPress Multisite con varios sitios. He configurado DISABLE_WP_CRON
como true
en mi archivo wp-config.php
.
Si configuramos una tarea cron con wget o curl, tenemos una limitación de 30 segundos para ejecutar el script PHP. Este tiempo es insuficiente para enviar grandes cantidades de notificaciones por correo y realizar otras tareas (quizás la conexión con el servidor SMTP remoto es lenta, o hay una gran cantidad de notificaciones de bbPress).
¿Podríamos usar algo como esto?
php -q wp-cron.php
Pero esto solo ejecuta el cron para un sitio en Multisite (cada sitio tiene sus propias tareas cron en diferentes tablas de MySQL).
P.D.: En el foro de wpmudev.org encontré una "solución" extraña que también usa Curl.
Otra P.D.: WP CLI tiene comandos excelentes para wp cron
pero solo permite ejecutar tareas cron manualmente (sí, podemos usar el atributo --url
). Por ejemplo:
wp cron event list --url=multisite.com
wp cron event list --url=subdominio.multisite.com
Después de haber agregado la constante en wp-config.php
defined('DISABLE_WP_CRON') or define('DISABLE_WP_CRON', true);
WP-CLI
Asumiendo que tienes tu archivo config.yml
configurado correctamente, puedes omitir el flag --path
al llamar a cron run
.
wp cron event run --due-now
[<hook>…]
Uno o más hooks para ejecutar.
[--due-now]
Ejecutar todos los hooks que estén pendientes ahora.
[--all]
Ejecutar todos los hooks.
Para ejecutar todas las tareas cron pendientes en orden:
function run_crons_due_now_in_order { for SITE_URL in $(wp site list --fields=url --format=csv | tail -n +2 | sort); do wp cron event run --due-now --url="$SITE_URL" && echo -e "\t+ Finished crons for $SITE_URL"; done; echo "Done"; }; run_crons_due_now_in_order;
Si prefieres ejecutarlas concurrentemente (ejecutando primero el cron no específico del sitio):
function run_all_crons_due_now { for SITE_URL in $(wp site list --fields=url --format=csv | tail -n +2 | sort); do wp cron event run --due-now --url="$SITE_URL" && echo -e "\t+ Finished crons for $SITE_URL" & done; wait $(jobs -p); echo "Done"; }; run_all_crons_due_now;
Deberías guardar cualquiera de las opciones en un archivo ejecutable
chmod +x run_all_wp_cron_events_due_now.sh
agregar una tarea al crontab
crontab -e
y probablemente ejecutarla cada minuto
* * * * * run_all_wp_cron_events_due_now.sh > /dev/null
Si necesitas ejecutar un comando personalizado desde cron, quizás debas especificar las rutas completas para que wp-cli funcione.
* * * * * cd /home/username/public_html; /usr/local/bin/php /home/username/wp-cli.phar your-custom-cron-commands run >/dev/null 2>&1
PHP
La única razón por la que necesitarías cargar WordPress aquí es para obtener las URLs desde la base de datos en lugar de usar una lista predefinida. Solo vamos a hacer ping a esas URLs y no nos importa realmente la respuesta.
custom-cron.php
<?php
// Cargar WordPress
require_once( dirname( __FILE__ ) . '/wp-load.php' );
// Verificar versión
global $wp_version;
$gt_4_6 = version_compare( $wp_version, '4.6.0', '>=' );
// Obtener blogs
$args = array( 'archived' => 0, 'deleted' => 0, 'public' => 1 );
$blogs = $gt_4_6 ? get_sites( $args ) : @wp_get_sites( $args ); // >= 4.6
// Ejecutar Cron en cada blog
echo "Ejecutando Crons: " . PHP_EOL;
$agent = 'WordPress/' . $wp_version . '; ' . home_url();
$time = time();
foreach ( $blogs as $blog ) {
$domain = $gt_4_6 ? $blog->domain : $blog['domain'];
$path = $gt_4_6 ? $blog->path : $blog['path'];
$command = "http://" . $domain . ( $path ? $path : '/' ) . 'wp-cron.php?doing_wp_cron=' . $time . '&ver=' . $wp_version;
$ch = curl_init( $command );
$rc = curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
$rc = curl_exec( $ch );
curl_close( $ch );
print_r( $rc );
print_r( "\t✔ " . $command . PHP_EOL );
}
Y agregar una única llamada a tu archivo custom-cron.php
en el crontab
* * * * * wget -q -O - http://your-site.com/custom-cron.php?doing_wp_cron

Creo que la mejor manera es usar WP-CLI pero necesitarías escribir un script bash para hacer esto. Aquí tienes uno que debería funcionar:
WP_PATH="/ruta/hacia/wp"
for SITE_URL in = $(wp site list --fields=domain,path,archived,deleted --format=csv --path="$WP_PATH" | grep ",0,0$" | awk -F ',' '{print $1 $2}')
do
for EVENT_HOOK in $(wp cron event list --format=csv --fields=hook,next_run_relative --url="$SITE_URL" --path="$WP_PATH" | grep \"now\"$ | awk -F ',' '{print $1}')
do
wp cron event run "$EVENT_HOOK" --url="$SITE_URL" --path="$WP_PATH"
done
done
Luego necesitarías agregar este script al crontab y ejecutarlo quizás cada minuto si lo deseas

Hm, gracias por tu opinión pero creo que esta es una solución un poco sucia (hacky). Creo que sería mejor escribir algún complemento PHP para wpcli que ejecute todas las tareas necesarias. Necesito más tiempo para entender las funciones y el código de WP para hacerlo.

@KolyaKorobochkin No estoy de acuerdo. Esto no es nada sucio ya que WP CLI fue hecho para usarse en la línea de comandos y en scripts bash.

wp cron event run --due-now --url="$SITE_URL" --path="$WP_PATH
- Según la documentación de run
, deberías utilizar el flag --due-now
. Esto reduciría las búsquedas y llamadas individuales a eventos. Ver: http://wp-cli.org/commands/cron/event/run/. De cualquier manera, estoy con @OmarJackman - está utilizando la línea de comandos para usar herramientas de WordPress en línea de comandos. Felicitaciones por un ejemplo limpio de Bash. Podrías complementar esta respuesta mostrando la entrada en el crontab.

Para beneficio de los lectores futuros, el script entonces luce así: WP_PATH="/path/to/wp"; for SITE_URL in $(wp site list --fields=domain,path,archived,deleted --format=csv --path="$WP_PATH" | grep ",0,0$" | awk -F ',' '{print $1 $2}'); do wp cron event run --due-now --url="$SITE_URL" --path="$WP_PATH"; done

Exactamente lo que estaba buscando - funciona tal como se anuncia, ¡gracias!

Este es un uso mucho más inteligente de las herramientas disponibles en WP CLI que las respuestas que hacen un gran esfuerzo para separar los resultados de la salida CSV. ¡Funciona genial!

Esta es la respuesta más inteligente. Y puedes poner esto directamente en tu crontab.

¡Muy inteligente! Estamos usando esto en SlickStack para Multisite ahora, cuando los usuarios seleccionan el método wpcli
para administrar su WP-Cron: https://github.com/littlebizzy/slickstack/blob/master/crons/01-cron-minutely.txt

Esta es mi solución:
global $multisite_hosts;
$multisite_hosts = Array('xxxx.dev.xxx.oondeo.es','x2.dev.xxx.oondeo.es','x3.dev.xxx.oondeo.es');
function run_cron(){
global $multisite_hosts;
$host=array_pop($multisite_hosts);
if (!$host)
return;
register_shutdown_function('shutdown');
if (!isset($_SERVER['HTTP_HOST'])) {
$_SERVER['HTTP_HOST'] = $host; // reemplazar con el host principal
}
require './wp-cron.php';
}
function shutdown()
{
run_cron();
}
run_cron();
Llamamos a esto desde crontab, espero que ayude

Puedes encontrar una guía completa aquí sobre cómo configurar correctamente el trabajo cron para un sistema WordPress Multisite, de modo que se ejecute en todos los sub-sitios y active los trabajos cron. https://support.shorturl.gg/business-marketing-and-seo-forums/topic/you-are-using-wp-cron-incorrectly-in-wordpress-multisite/
Usar el trabajo cron estándar para sitios web WordPress independientes no funciona bien, ya que el trabajo cron estándar de WP solo activará el cron para el sitio principal y, por lo tanto, no se ejecutarán trabajos cron en los sub-sitios.

Aunque este enlace puede responder a la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas que solo contienen enlaces pueden volverse inválidas si la página enlazada cambia. - De la revisión

Creo que primero debes explicarnos a qué te refieres con "ejecutar wp-cron.php de la manera correcta". Según WordPress, no tiene sentido que deshabilites wp-cron y aún así quieras que se ejecute... Desde la perspectiva de Linux, usar wget o /bin/php en wp-cron.php sería lo correcto, pero parece que tu host en particular está limitando el número de invocaciones de PHP por razones de seguridad? -- esa parte no queda completamente clara en tu descripción.
Otra pregunta: ¿a cuántos emails te refieres con "un montón"? Hay buenas razones por las que no deberías intentar enviar demasiados emails tan rápidamente.
Puede que tengas que reconsiderar tus objetivos.
(Comentaría en lugar de responder, pero no tengo suficiente reputación en wpstack.)

Existen múltiples formas de ejecutar el cron de WordPress. El mecanismo incorporado se activa mediante visitas a la página, y es una práctica común establecer DISABLE_WP_CRON
para deshabilitar este método cuando se configura el método más confiable de cron del sistema en su lugar.

La pregunta no es por qué hacerlo/si las razones son significativas, sino cómo llamar correctamente a wp-cron.php en una instalación multisitio. Bastante claro para mí ;)
