Cómo ejecutar un trabajo PHP varias veces en un minuto con Crontab en Ubuntu 20.04
La autora seleccionó a Girls Who Code para recibir una donación como parte del programa Write for DOnations .
Introducción
En Linux, puedes usar la versátil herramienta crontab para procesar tareas de larga duración en segundo plano en momentos específicos. Si bien el demonio es excelente para ejecutar tareas repetitivas, tiene una limitación: solo puedes ejecutar tareas en un intervalo de tiempo mínimo de 1 minuto.
Sin embargo, en muchas aplicaciones, para evitar una mala experiencia del usuario, es preferible que los trabajos se ejecuten con mayor frecuencia. Por ejemplo, si utiliza el modelo de cola de trabajos para programar tareas de procesamiento de archivos en su sitio web, una espera significativa tendrá un impacto negativo en los usuarios finales.
Otro escenario es una aplicación que utiliza el modelo de cola de trabajos para enviar mensajes de texto o correos electrónicos a los clientes una vez que han completado una determinada tarea en una aplicación (por ejemplo, enviar dinero a un destinatario). Si los usuarios tienen que esperar un minuto antes de recibir un mensaje de confirmación, podrían pensar que la transacción falló e intentar repetirla.
Para superar estos desafíos, puede programar un script PHP que ejecute bucles y procese tareas de forma repetitiva durante 60 segundos mientras espera que el demonio crontab lo vuelva a llamar después de un minuto. Una vez que el demonio crontab llama al script PHP por primera vez, puede ejecutar tareas en un período de tiempo que coincida con la lógica de su aplicación sin hacer esperar al usuario.
En esta guía, creará una cron_jobs
base de datos de muestra en un servidor Ubuntu 20.04. Luego, configurará una tasks
tabla y un script que ejecute los trabajos en su tabla en intervalos de 5 segundos utilizando el while(...){...}
bucle y sleep()
las funciones de PHP.
Prerrequisitos
Para completar este tutorial, necesitará lo siguiente:
-
Un servidor Ubuntu 20.04 configurado con un usuario que no sea root. Siga nuestra guía de configuración inicial del servidor con Ubuntu 20.04 .
-
Una pila LAMP configurada en su servidor. Consulte la guía Cómo instalar la pila Linux, Apache, MySQL, PHP (LAMP) en Ubuntu 20.04 . Para este tutorial, puede omitir el Paso 4: Creación de un host virtual para su sitio web .
Paso 1: Configuración de una base de datos
En este paso, creará una base de datos y una tabla de muestra. Primero, SSH
acceda a su servidor e inicie sesión en MySQL como raíz:
- sudo mysql -u root -p
Ingrese su contraseña raíz para el servidor MySQL y presione ENTER
continuar. Luego, ejecute el siguiente comando para crear una cron_jobs
base de datos.
- CREATE DATABASE cron_jobs;
Cree un usuario que no sea root para la base de datos. Necesitará las credenciales de este usuario para conectarse a la cron_jobs
base de datos desde PHP. Recuerde reemplazar EXAMPLE_PASSWORD
por un valor seguro:
- CREATE USER 'cron_jobs_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
- GRANT ALL PRIVILEGES ON cron_jobs.* TO 'cron_jobs_user'@'localhost';
- FLUSH PRIVILEGES;
A continuación, cambie a la cron_jobs
base de datos:
- USE cron_jobs;
OutputDatabase changed
Una vez que haya seleccionado la base de datos, cree una tasks
tabla. En esta tabla, insertará algunas tareas que se ejecutarán automáticamente mediante un trabajo cron. Dado que el intervalo de tiempo mínimo para ejecutar un trabajo cron es de 1
un minuto, más adelante codificará un script PHP que anule esta configuración y, en su lugar, ejecute los trabajos en intervalos de 5 segundos.
Por ahora, crea tu tasks
tabla:
- CREATE TABLE tasks (
- task_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
- task_name VARCHAR(50),
- queued_at DATETIME,
- completed_at DATETIME,
- is_processed CHAR(1)
- ) ENGINE = InnoDB;
NOW()
Inserte tres registros en la tabla de tareas. Utilice la función MySQL en la queued_at
columna para registrar la fecha y hora actuales en las que se ponen en cola las tareas. Además, para la completed_at
columna, utilice la CURDATE()
función MySQL para establecer una hora predeterminada de 00:00:00
. Más adelante, a medida que se completen las tareas, su secuencia de comandos actualizará esta columna:
- INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 1', NOW(), CURDATE(), 'N');
- INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 2', NOW(), CURDATE(), 'N');
- INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 3', NOW(), CURDATE(), 'N');
Confirme la salida después de ejecutar cada INSERT
comando:
OutputQuery OK, 1 row affected (0.00 sec)...
Asegúrese de que los datos estén en su lugar ejecutando una SELECT
declaración en la tasks
tabla:
- SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;
Encontrará una lista de todas las tareas:
Output+---------+-----------+---------------------+---------------------+--------------+| task_id | task_name | queued_at | completed_at | is_processed |+---------+-----------+---------------------+---------------------+--------------+| 1 | TASK 1 | 2021-03-06 06:27:19 | 2021-03-06 00:00:00 | N || 2 | TASK 2 | 2021-03-06 06:27:28 | 2021-03-06 00:00:00 | N || 3 | TASK 3 | 2021-03-06 06:27:36 | 2021-03-06 00:00:00 | N |+---------+-----------+---------------------+---------------------+--------------+3 rows in set (0.00 sec)
El tiempo de la completed_at
columna se establece en 00:00:00
, esta columna se actualizará una vez que las tareas sean procesadas por un script PHP que creará a continuación.
Salir de la interfaz de línea de comandos de MySQL:
- QUIT;
OutputBye
Su cron_jobs
base de datos y tasks
tabla ahora están en su lugar y ahora puede crear un script PHP que procese los trabajos.
Paso 2: Creación de un script PHP que ejecute tareas después de 5 segundos
En este paso, creará un script que utiliza una combinación del while(...){...}
bucle PHP y sleep
funciones para ejecutar tareas cada 5 segundos.
Abra un nuevo /var/www/html/tasks.php
archivo en el directorio raíz de su servidor web usando nano:
- sudo nano /var/www/html/tasks.php
A continuación, crea un nuevo try {
bloque después de una ?php
etiqueta y declara las variables de base de datos que creaste en el Paso 1. Recuerda reemplazarlas EXAMPLE_PASSWORD
con la contraseña real de tu usuario de base de datos:
/var/www/html/tareas.php
?phptry { $db_name = 'cron_jobs'; $db_user = 'cron_jobs_user'; $db_password = 'EXAMPLE_PASSWORD'; $db_host = 'localhost';
A continuación, declara una nueva clase PDO (objeto de datos PHP) y establece el atributo ERRMODE_EXCEPTION
para detectar cualquier error de PDO. Además, cambia ATTR_EMULATE_PREPARES
a false
para permitir que el motor de base de datos MySQL nativo se encargue de la emulación. Las declaraciones preparadas te permiten enviar las consultas SQL y los datos por separado para mejorar la seguridad y reducir las posibilidades de un ataque de inyección SQL:
/var/www/html/tareas.php
$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo-setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Luego, crea una nueva variable llamada $loop_expiry_time
y configúrala con la hora actual más 60 segundos. Luego, abre una nueva while(time() $loop_expiry_time) {
declaración PHP. La idea aquí es crear un bucle que se ejecute hasta que la hora actual ( time()
) coincida con la variable $loop_expiry_time
:
[label /var/www/html/tasks.php] $loop_expiry_time = time() + 60; while (time() $loop_expiry_time) {
A continuación, declare una declaración SQL preparada que recupere trabajos no procesados de la tasks
tabla:
[label /var/www/html/tasks.php] $data = []; $sql = "select task_id from tasks where is_processed = :is_processed ";
Ejecute el comando SQL y obtenga todas las filas de la tasks
tabla que tengan la columna is_processed
establecida en N
. Esto significa que las filas no se procesan:
[label /var/www/html/tasks.php] $data['is_processed'] = 'N'; $stmt = $pdo-prepare($sql); $stmt-execute($data);
A continuación, recorra las filas recuperadas mediante una while ($row = $stmt-fetch(PDO::FETCH_ASSOC)) {...}
sentencia PHP y cree otra sentencia SQL. Esta vez, el comando SQL actualiza las columnas is_processed
y completed_at
para cada tarea procesada. Esto garantiza que no procese las tareas más de una vez:
[label /var/www/html/tasks.php] while ($row = $stmt-fetch(PDO::FETCH_ASSOC)) { $data_update = []; $sql_update = "update tasks set is_processed = :is_processed, completed_at = :completed_at where task_id = :task_id "; $data_update = [ 'is_processed' = 'Y', 'completed_at' = date("Y-m-d H:i:s"), 'task_id' = $row['task_id'] ]; $stmt = $pdo-prepare($sql_update); $stmt-execute($data_update); }
Nota: Si tiene una cola grande para procesar (por ejemplo, 100 000 registros por segundo), puede considerar poner en cola los trabajos en un servidor Redis, ya que es más rápido que MySQL cuando se trata de implementar el modelo de cola de trabajos. Sin embargo, esta guía procesará un conjunto de datos más pequeño.
Antes de cerrar la primera while (time() $loop_expiry_time) {
declaración PHP, incluya una sleep(5);
declaración para pausar la ejecución de los trabajos durante 5 segundos y liberar recursos del servidor.
Puede cambiar el período de 5 segundos según la lógica de su empresa y la velocidad con la que desea que se ejecuten las tareas. Por ejemplo, si desea que las tareas se procesen 3 veces en un minuto, configure este valor en 20 segundos.
Recuerde catch
cualquier mensaje de error PDO dentro de un } catch (PDOException $ex) { echo $ex-getMessage(); }
bloque:
[label /var/www/html/tasks.php] sleep(5); } } catch (PDOException $ex) { echo $ex-getMessage(); }
Su tasks.php
expediente completo quedará como sigue:
/var/www/html/tareas.php
?phptry { $db_name = 'cron_jobs'; $db_user = 'cron_jobs_user'; $db_password = 'EXAMPLE_PASSWORD'; $db_host = 'localhost'; $pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo-setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $loop_expiry_time = time() + 60; while (time() $loop_expiry_time) { $data = []; $sql = "select task_id from tasks where is_processed = :is_processed "; $data['is_processed'] = 'N'; $stmt = $pdo-prepare($sql); $stmt-execute($data); while ($row = $stmt-fetch(PDO::FETCH_ASSOC)) { $data_update = []; $sql_update = "update tasks set is_processed = :is_processed, completed_at = :completed_at where task_id = :task_id "; $data_update = [ 'is_processed' = 'Y', 'completed_at' = date("Y-m-d H:i:s"), 'task_id' = $row['task_id'] ]; $stmt = $pdo-prepare($sql_update); $stmt-execute($data_update); } sleep(5); } } catch (PDOException $ex) { echo $ex-getMessage(); }
Guarde el archivo presionando CTRL
+ X
, Y
luego ENTER
.
Una vez que haya terminado de codificar la lógica en el /var/www/html/tasks.php
archivo, programará el demonio crontab para que ejecute el archivo cada 1 minuto en el siguiente paso.
Paso 3: Programar la ejecución del script PHP después de 1 minuto
En Linux, puedes programar trabajos para que se ejecuten automáticamente después de un tiempo estipulado ingresando un comando en el archivo crontab. En este paso, le indicarás al demonio crontab que ejecute tu /var/www/html/tasks.php
script después de cada minuto. Por lo tanto, abre el /etc/crontab
archivo usando nano:
- sudo nano /etc/crontab
Luego agregue lo siguiente hacia el final del archivo para ejecutarlo http://localhost/tasks.php
después de cada 1
minuto:
/etc/crontab
...* * * * * root /usr/bin/wget -O - http://localhost/tasks.php
Guarde y cierre el archivo.
Esta guía asume que tienes conocimientos básicos sobre cómo funcionan los trabajos cron. Considera leer nuestra guía sobre Cómo usar Cron para automatizar tareas en Ubuntu .
Como se indicó anteriormente, aunque el demonio cron ejecuta el tasks.php
archivo cada 1 minuto, una vez que el archivo se ejecuta por primera vez, repetirá las tareas abiertas durante otros 60 segundos. Cuando expire el tiempo del bucle, el demonio cron ejecutará el archivo nuevamente y el proceso continuará.
Después de actualizar y cerrar el /etc/crontab
archivo, el demonio crontab debería comenzar a ejecutar las tareas MySQL que insertó en la tasks
tabla de inmediato. Para confirmar si todo está funcionando como se espera, consultará su cron_jobs
base de datos a continuación.
Paso 4: Confirmación de la ejecución del trabajo
En este paso, abrirá su base de datos una vez más para verificar si el tasks.php
archivo está procesando trabajos en cola cuando lo ejecuta automáticamente crontab.
Vuelva a iniciar sesión en su servidor MySQL como root:
- sudo mysql -u root -p
Luego, ingrese la contraseña raíz de su servidor MySQL y presione ENTER
para continuar. Luego, cambie a la base de datos:
- USE cron_jobs;
OutputDatabase changed
Ejecute una SELECT
declaración contra la tasks
tabla:
SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;
Recibirá un resultado similar al siguiente. En la completed_at
columna, las tareas se han procesado a intervalos de 5
segundos. Además, las tareas se han marcado como completadas, ya que la is_processed
columna ahora está configurada en Y
, lo que significa YES
.
Output+---------+-----------+---------------------+---------------------+--------------+| task_id | task_name | queued_at | completed_at | is_processed |+---------+-----------+---------------------+---------------------+--------------+| 1 | TASK 1 | 2021-03-06 06:27:19 | 2021-03-06 06:30:01 | Y || 2 | TASK 2 | 2021-03-06 06:27:28 | 2021-03-06 06:30:06 | Y || 3 | TASK 3 | 2021-03-06 06:27:36 | 2021-03-06 06:30:11 | Y |+---------+-----------+---------------------+---------------------+--------------+3 rows in set (0.00 sec)
Esto confirma que su script PHP está funcionando como se esperaba; ha ejecutado tareas en un intervalo de tiempo más corto anulando la limitación del período de tiempo de 1 minuto establecido por el demonio crontab.
Conclusión
En esta guía, ha configurado una base de datos de muestra en un servidor Ubuntu 20.04. Luego, ha creado trabajos en una tabla y los ha ejecutado en intervalos de 5 segundos mediante el while(...){...}
bucle y sleep()
las funciones de PHP. Utilice la lógica de este tutorial la próxima vez que implemente una aplicación basada en cola de trabajos en la que las tareas deban ejecutarse varias veces en un período de tiempo de 1 minuto.
Para obtener más tutoriales de PHP, consulte nuestra página de temas de PHP .
Deja una respuesta