Cómo escribir trabajos cron utilizando sentencias crontab y programarlos con el node-cron
paquete.
Primeros pasos
Para este tutorial, vamos a utilizar el marco JavaScript de pila completa de CheatCode, Joystick. Joystick reúne un marco de interfaz de usuario de front-end con un back-end de Node.js para crear aplicaciones.
Para comenzar, querremos instalar Joystick a través de NPM. Asegúrese de estar usando Node.js 16+ antes de instalar para garantizar la compatibilidad (lea este tutorial primero si necesita aprender a instalar Node.js o ejecutar varias versiones en su computadora):
Terminal
npm i -g @joystick.js/cli
Esto instalará Joystick globalmente en su computadora. Una vez instalado, vamos a crear un nuevo proyecto:
Terminal
joystick create app
Después de unos segundos, verá un mensaje de cierre de sesión en cd
en su nuevo proyecto y ejecute joystick start
. Antes de hacerlo, necesitamos instalar una dependencia:node-cron
.
Terminal
npm i node-cron
Después de que esté instalado, continúe e inicie su servidor:
Terminal
cd app && joystick start
Después de esto, su aplicación debería estar ejecutándose y estamos listos para comenzar.
¿Qué es un trabajo cron?
Un trabajo cron o "trabajo cronológico" (tomado del nombre de la herramienta crontab original que inventó el concepto de trabajo cron) es una tarea automatizada que se ejecuta en un momento específico o en un intervalo específico. Por ejemplo, en el mundo físico, es posible que te despiertes todos los días y sigas una rutina como:
- Dúchate (6:00 a. m.)
- Cepille sus dientes (6:15 AM)
- Vístete (6:30 a. m.)
- Desayunar (6:40 a. m.)
Cada parte de esa rutina es un "trabajo". Todos los días, "completa" o "ejecuta" ese trabajo. Lo más probable es que hagas estas mismas cosas aproximadamente a la misma hora todos los días.
De manera similar, en una aplicación, es posible que tenga alguna tarea que deba realizarse todos los días o en un momento específico, por ejemplo:
- Envíe un correo electrónico con el tráfico del día anterior, todos los días a las 12:00 a. m.
- Cada tres horas, borre los datos temporales de una tabla/colección de la base de datos.
- Una vez por semana, obtenga la última lista de precios de la API de un proveedor.
Cada uno de estos son trabajos que deben realizarse en nuestra aplicación. Debido a que no queremos ejecutarlos manualmente (o tenemos que recordar ejecutarlos), podemos escribir un cron trabajo en nuestro código que lo hace automáticamente por nosotros.
Los trabajos cron se pueden programar de una de dos maneras:automáticamente cuando iniciamos nuestra aplicación o bajo demanda a través de una llamada de función.
Cableando un trabajo cron
Afortunadamente, los trabajos cron son de naturaleza simple. Constan de dos partes clave:
- Una declaración crontab que describe cuándo debe ejecutarse un trabajo.
- Una función para llamar cuando la hora actual coincida con la instrucción crontab.
Para comenzar, escribiremos una función que pueda ejecutar varios trabajos cron por nosotros y luego veremos cómo conectar cada trabajo individual:
/api/cron/index.js
export default () => {
// We'll write our cron jobs here...
}
No hay mucho aquí, solo una simple función de flecha. Nuestro objetivo será definir nuestros trabajos cron dentro de esta función y luego llamar a esta función cuando se inicie nuestro servidor de aplicaciones. Esto es intencional porque queremos asegurarnos de que nuestra aplicación esté funcionando antes programamos cualquier trabajo cron (para evitar contratiempos y asegurarnos de que el código del que dependen nuestros trabajos esté disponible).
Muy rápido, veamos cómo vamos a llamar a esto en el inicio del servidor:
/index.servidor.js
import node from "@joystick.js/node";
import api from "./api";
import cron from './api/cron';
node.app({
api,
routes: {
"/": (req, res) => { ... },
}).then(() => {
cron();
});
En el index.server.js
archivo aquí (creado para nosotros cuando ejecutamos joystick create
arriba), hemos hecho un pequeño cambio.
Al final de la llamada a node.app()
—la función que inicia nuestra aplicación en Joystick— hemos agregado un .then()
llamar de vuelta. Estamos usando esto porque esperamos node.app()
para devolvernos una Promesa de JavaScript. Aquí, .then()
está diciendo "después de node.app()
se ha ejecutado y resuelto, llame a esta función".
En este código, "esta función" es la función que estamos pasando a .then()
. Esta función se llama inmediatamente después de node.app()
resuelve (es decir, JavaScript Promise ha señalado que su trabajo está completo y nuestro código puede continuar).
En la parte superior de nuestro archivo, hemos importado nuestro cron()
función que especificamos en /api/cron/index.js
. Dentro de nuestro .then()
devolución de llamada, llamamos a esta función para iniciar nuestros trabajos cron después de que se inicie el servidor.
/api/cron/index.js
import cron from 'node-cron';
import { EVERY_30_SECONDS, EVERY_MINUTE, EVERY_30_MINUTES, EVERY_HOUR } from './scheduleConstants';
export default () => {
cron.schedule(EVERY_30_SECONDS, () => {
// We'll do some work here...
});
cron.schedule(EVERY_MINUTE, () => {
// We'll do some work here...
});
cron.schedule(EVERY_30_MINUTES, () => {
// We'll do some work here...
});
cron.schedule(EVERY_HOUR, () => {
// We'll do some work here...
});
}
De vuelta en nuestro /api/cron/index.js
file completamos un poco nuestra función. Ahora, arriba, podemos ver que hemos importado el cron
objeto del node-cron
paquete que instalamos anteriormente.
Abajo en nuestra función exportada, llamamos al cron.schedule()
función que toma dos argumentos:
- La declaración crontab que define la programación para el trabajo cron.
- Una función para llamar cuando llega el tiempo especificado por el programa.
En la parte superior de nuestro archivo, podemos ver algunas variables con nombre que se importan de un archivo que necesitamos crear en el /api/cron
carpeta:scheduleConstants.js
.
/api/cron/scheduleConstants.js
// NOTE: These can be easily generated with https://crontabkit.com/crontab-expression-generator
export const EVERY_30_SECONDS = '*/30 * * * * *';
export const EVERY_MINUTE = '* * * * * ';
export const EVERY_30_MINUTES = '*/30 * * * *';
export const EVERY_HOUR = '0 0 * * * *';
Aquí, tenemos cuatro instrucciones crontab diferentes, cada una especificando un horario diferente. Para que todo sea más fácil de entender en nuestro código, en este archivo estamos asignando un nombre amigable para los humanos a cada declaración para que podamos interpretar rápidamente la programación en nuestro código.
Las declaraciones crontab tienen una sintaxis única que involucra asteriscos (o "estrellas", si lo prefiere) donde cada estrella representa una unidad de tiempo. En orden, de izquierda a derecha, las estrellas representan:
- Minuto
- Segundo
- Hora
- Día del mes
- Mes
- Día de la semana
Como vemos arriba, cada estrella se puede reemplazar con números y caracteres para especificar ciertos intervalos de tiempo. Este es un gran tema, por lo que si tiene curiosidad sobre el funcionamiento interno de crontab, se recomienda que lea esta guía.
/api/cron/index.js
import cron from 'node-cron';
import fs from 'fs';
import { EVERY_30_SECONDS, EVERY_MINUTE, EVERY_30_MINUTES, EVERY_HOUR } from './scheduleConstants';
const generateReport = (interval = '') => {
if (!fs.existsSync('reports')) {
fs.mkdirSync('reports');
}
const existingReports = fs.readdirSync('reports');
const reportsOfType = existingReports?.filter((existingReport) => existingReport.includes(interval));
fs.writeFileSync(`reports/${interval}_${new Date().toISOString()}.txt`, `Existing Reports: ${reportsOfType?.length}`);
};
export default () => {
cron.schedule(EVERY_30_SECONDS, () => {
generateReport('thirty-seconds');
});
cron.schedule(EVERY_MINUTE, () => {
generateReport('minute');
});
cron.schedule(EVERY_30_MINUTES, () => {
generateReport('thirty-minutes');
});
cron.schedule(EVERY_HOUR, () => {
generateReport('hour');
});
}
De vuelta en nuestro código, ahora estamos listos para usar nuestros trabajos cron. Como vimos antes, estamos importando nuestras declaraciones crontab con nombre de /api/cron/scheduleConstants.js
y pasándolos como primer argumento a cron.schedule()
.
Ahora, estamos listos para hacer un trabajo real... o al menos, algo falso trabajo.
Arriba de nuestra función exportada y justo debajo de nuestras importaciones, hemos agregado una función generateReport()
para simular el trabajo de "generar un informe" en algún intervalo. Esa función toma un interval
arbitrario nombre e intenta crear un archivo en el reports
directorio de nuestra aplicación. El nombre de cada archivo toma la forma de <interval>_<timestamp>.txt
donde <interval>
es el interval
nombre que pasamos al generateReport()
función y <timestamp>
es la cadena de fecha ISO-8601 que marca cuando se creó el archivo.
Para llegar allí, primero, nos aseguramos de que el reports
El directorio realmente existe (requerido ya que obtendremos un error si intentamos escribir un archivo en una ubicación que no existe). Para hacer eso, arriba, hemos importado fs
del fs
paquete:un paquete central de Node.js que se utiliza para interactuar con el sistema de archivos.
De ese paquete, usamos fs.existsSync()
para ver si el reports
directorio existe. Si no , seguimos adelante y lo creamos.
Si lo hace existe, a continuación, leemos el contenido actual del directorio (una lista de matriz de todos los archivos dentro del directorio) como existingReports
y luego tome esa lista y fíltrela por interval
escribe usando JavaScript Array.filter
función.
Con todo esto, intentamos escribir nuestro archivo usando el <interval>_<timestamp>.txt
patrón que describimos anteriormente como el nombre del archivo, y establecer el contenido de ese archivo igual a una cadena que dice Existing Reports: <count>
donde <count>
es igual al número existente de informes de interval
escriba en el momento de la generación (por ejemplo, para el primer informe es 0
, para el siguiente es 1
, etc.).
¡Eso es todo! Ahora, cuando iniciemos nuestro servidor, deberíamos ver nuestros trabajos cron ejecutándose y los informes apareciendo en el /reports
directorio.
Terminando
En este tutorial, aprendimos a escribir y programar trabajos cron en Node.js usando el node-cron
paquete. Aprendimos cómo organizar nuestro código de trabajo cron y asegurarnos de llamarlo después de que se inicie nuestra aplicación. También aprendimos cómo funcionan las instrucciones crontab y cómo escribir varios trabajos cron usando constantes escritas previamente que hacen que nuestras declaraciones crontab sean más fáciles de entender.