Cómo ejecutar cron más de una vez por minuto usando PHP

El archivo de configuración clásico para las entradas de trabajos cron en el sistema operativo Linux es el siguiente:

# ┌─────────────  (0 - 59)
# │ ┌─────────────  (0 - 23)
# │ │ ┌─────────────  (1 - 31)
# │ │ │ ┌─────────────  (1 - 12)
# │ │ │ │ ┌─────────────   (0 - 6)
# │ │ │ │ │
# * * * * * <  >

Los primeros cinco parámetros indican el tiempo de ejecución de esta tarea, y el sexto, el comando en sí, que debe ejecutarse. Los parámetros de tiempo son: minutos, horas, días, meses y día de la semana. Además, todos los números deben representarse como enteros o en forma de sintaxis especial .

Como resultado, el intervalo mĂ­nimo posible para iniciar un comando es una vez por minuto.

Para muchas tareas, la ejecución de comandos se necesita con mucha más frecuencia, por ejemplo, cada 10 segundos. Para algunas tareas de automatización de procesos de negocio, el retraso máximo permitido a menudo no es más de 1-1.5 segundos.

Por supuesto, el cron clásico no es adecuado para esto: debe mejorarse.

La siguiente es una implementación paso a paso de crear funcionalidades adicionales (en PHP) para el cron clásico en Linux usando protección adicional contra reiniciar procesos.

ConfiguraciĂłn de tareas y configuraciĂłn cron


Por ejemplo, utilizaremos la siguiente tarea:

  1. En la parte frontal, el usuario puede iniciar la ejecuciĂłn de una tarea compleja haciendo clic en el botĂłn "Ejecutar";
  2. El back end después de escribir una nueva línea en la base de datos informa al usuario de la confirmación;
  3. A través de cron, "rastrearemos" esas nuevas tareas y las completaremos lo más rápido posible para que el usuario no obtenga el resultado en un minuto, sino de inmediato *.

* Si usa el lanzamiento de comandos, solo por minuto, la tarea comenzará cuando los segundos lleguen al cero más cercano (el comienzo de un nuevo minuto). Por lo tanto, en la forma clásica, el usuario deberá esperar la ejecución de 0 a 59 segundos.

Entonces, cron se configurará en su forma máxima, es decir una vez por minuto

* * * * * /usr/bin/php -q /run.php > /dev/null 2>&1
#/usr/bin/php -    PHP   (      )
#/run.php -   PHP   
#> /dev/null 2>&1 - ,            run.php

Ciclo Ăşnico


Inicialmente, debe decidir con qué frecuencia solicitaremos nuevas tareas en la base de datos, dependiendo de esto, el número de ciclos y la suspensión lógica (función sleep) cambiarán.

En el ejemplo actual, se utiliza un paso de 10 segundos. Por lo tanto, el nĂşmero de ciclos es 60/10 = 6. Total, el cĂłdigo general es el siguiente:

for ($cycle = 1; $cycle <= 6; $cycle++) { # 6 
    $all_tasks = get_all_tasks(); #     
    if ($all_tasks) { #    - 
        foreach($all_tasks as $one_task) { #     
            solve_one_task($one_task); #  
        }
    }
    sleep(10); #  ,  ""  10 
}

Aclaración : en este ejemplo, se utiliza un paso de 10 segundos, que puede proporcionar un intervalo mínimo de ejecución del script una vez cada 10 segundos. En consecuencia, para una ejecución más frecuente, debe cambiar el número de ciclos y el "tiempo de suspensión".

Evitar la ejecuciĂłn repetida de tareas


En la forma presentada, hay una incertidumbre, a saber, la ejecuciĂłn repetida de la tarea si ya se ha iniciado. Esto se vuelve especialmente cierto si la tarea es "difĂ­cil" y requiere unos segundos para implementarla.

Esto crea un problema de re-ejecuciĂłn:

  • La funciĂłn solve_one_task()ya se está ejecutando, pero aĂşn no ha completado su trabajo;
  • Por lo tanto, en la base de datos la tarea todavĂ­a está marcada como no cumplida;
  • El siguiente ciclo obtendrá esta tarea nuevamente y ejecutará la funciĂłn solve_one_task()nuevamente, con la misma tarea.

Por supuesto, esto se puede resolver, por ejemplo, cambiando algĂşn estado en la base de datos para esta tarea.

Pero no cargaremos la base de datos: segĂşn mis pruebas, MYSQL puede aceptar la solicitud, pero no procesarla de inmediato. Una diferencia de incluso 0,5 segundos puede conducir a la ejecuciĂłn repetida, lo que categĂłricamente no es adecuado.

También en este caso solo estamos hablando del estado de las tareas, por lo que es mejor usar el sistema de archivos del servidor.

El modelo de verificación básico se construye usando flockuna función que establece y desbloquea un archivo.

En la ejecuciĂłn de PHP, la funciĂłn se puede representar de la siguiente manera:

$lock_file_abs = 'file'; #  
$fp = fopen($lock_file_abs,"w+"); #   
if (flock($fp, LOCK_EX | LOCK_NB)) { #,    
    solve_one_task($one_task); #  
    flock($fp, LOCK_UN); #   ,     
}
else {
    #,   , ..       
}
fclose($fp); #   
unlink($lock_file_abs); # ,   

Resultado


La visiĂłn general de todo el ciclo es la siguiente:

for ($cycle = 1; $cycle <= 6; $cycle++) {
    $all_tasks = get_all_tasks();
    if ($all_tasks) {
        foreach($all_tasks as $one_task) {
            $lock_file_abs = __DIR__.'/locks/run_'.$one_task['id'];
            $fp = fopen($lock_file_abs,"w+");
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                solve_one_task($one_task);
                flock($fp, LOCK_UN);
            }
            else {
                #    
            }
            fclose($fp);
            unlink($lock_file_abs);
        }
    }
    sleep(10);
}

Por lo tanto, dicho algoritmo le permite iniciar una ejecución cíclica del procesamiento de tareas y no le preocupa que la tarea se procese más de una vez.

Y ahora es suficiente simplemente ejecutar este código a través de cron cada minuto, y, a su vez, ejecutará ciclos más pequeños ya dentro de sí mismo.

All Articles