JavaScript >> Javascript 文檔 >  >> Node.js

如何在 Node.js 中調度和運行 Cron 作業

如何使用 crontab 語句編寫 cron 作業並使用 node-cron 安排它們 包。

開始使用

在本教程中,我們將使用 CheatCode 的全棧 JavaScript 框架 Joystick。 Joystick 將前端 UI 框架與用於構建應用的 Node.js 後端結合在一起。

首先,我們要通過 NPM 安裝 Joystick。確保在安裝之前使用 Node.js 16+ 以確保兼容性(如果您需要學習如何安裝 Node.js 或在計算機上運行多個版本,請先閱讀本教程):

終端

npm i -g @joystick.js/cli

這將在您的計算機上全局安裝操縱桿。安裝好之後,接下來我們新建一個項目:

終端

joystick create app

幾秒鐘後,您將看到一條消息已註銷到 cd 進入你的新項目並運行 joystick start .在你做之前,我們需要安裝一個依賴:node-cron .

終端

npm i node-cron

安裝完成後,繼續啟動您的服務器:

終端

cd app && joystick start

在此之後,您的應用應該可以運行了,我們可以開始了。

什麼是 cron 作業?

cron 作業或“按時間順序排列的作業”(取自發明 cron 作業概念的原始 crontab 工具的名稱)是在特定時間或特定間隔運行的自動化任務。例如,在現實世界中,您可能每天醒來並遵循以下慣例:

  1. 洗澡(早上 6:00)
  2. 刷牙(早上 6 點 15 分)
  3. 穿好衣服(早上 6:30)
  4. 吃早餐(早上 6 點 40 分)

該例程的每個部分都是一項“工作”。每一天,你“完成”或“運行”那項工作。您很可能每天在大致相同的時間做同樣的事情。

與此類似,在一個應用程序中,您可能有一些任務需要每天或在特定時間執行,例如:

  1. 每天凌晨 12:00 發送一封關於前一天流量的電子郵件。
  2. 每三個小時,從數據庫表/集合中清除臨時數據。
  3. 每週一次,從供應商的 API 中獲取最新的價目表。

這些都是需要在我們的應用程序中執行的工作。因為我們不想手動運行它們(或者必須記住運行它們),所以我們可以編寫一個 cron 在我們的代碼中自動為我們完成這項工作。

Cron 作業可以通過以下兩種方式之一進行調度:在我們啟動應用程序時自動調度,或者通過函數調用按需調度。

連接 cron 作業

幸運的是,cron 作業本質上很簡單。它們由兩個關鍵部分組成:

  1. 描述 when 的 crontab 語句 作業應該運行。
  2. 噹噹前時間與 crontab 語句匹配時調用的函數。

首先,我們將編寫一個可以為我們運行多個 cron 作業的函數,然後看看如何連接每個單獨的作業:

/api/cron/index.js

export default () => {
  // We'll write our cron jobs here...
}

這裡沒什麼,只是一個簡單的箭頭函數。我們的目標是在這個函數中定義我們的 cron 作業,然後在我們的應用服務器啟動時調用這個函數。這是故意的,因為要確保我們的應用程序在之前啟動並運行 我們安排任何 cron 作業(以避免打嗝並確保我們的作業所依賴的代碼可用)。

很快,讓我們看看我們將如何在服務器啟動時調用它:

/index.server.js

import node from "@joystick.js/node";
import api from "./api";
import cron from './api/cron';

node.app({
  api,
  routes: {
    "/": (req, res) => { ... },
}).then(() => {
  cron();
});

index.server.js 此處的文件(在我們運行 joystick create 時為我們創建 以上),我們做了一個小改動。

在對 node.app() 的調用結束時 ——在 Joystick 中啟動我們的應用程序的函數——我們添加了一個 .then() 打回來。我們使用它是因為我們期望 node.app() 向我們返回一個 JavaScript Promise。這裡,.then() 說“在 node.app() 之後 已經運行並解決,調用這個函數。”

在這段代碼中,“這個函數”是我們傳遞給 .then() 的函數 .在 node.app() 之後立即調用此函數 解決(意思是,JavaScript Promise 已經表明它的工作已經完成,我們的代碼可以繼續)。

在我們文件的頂部,我們已經導入了 cron() 我們在 /api/cron/index.js 中指定的函數 .我們的 .then() 內部 回調,我們調用這個函數在服務器啟動後啟動我們的cron作業。

/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...
  });
}

回到我們的 /api/cron/index.js 文件我們填寫了我們的功能。現在,在頂部,我們可以看到我們已經導入了 cron node-cron 中的對象 我們之前安裝的包。

在我們導出的函數中,我們調用 cron.schedule() 接受兩個參數的函數:

  1. 定義 cron 作業計劃的 crontab 語句。
  2. 當計劃指定的時間發生時調用的函數。

在我們文件的頂部,我們可以看到一些命名變量從我們需要在 /api/cron 中創建的文件中導入 文件夾: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 * * * *';

在這裡,我們有四個不同的 crontab 語句,每個語句指定一個不同的計劃。為了讓我們的代碼更容易理解,在這個文件中,我們為每個語句分配了一個易於理解的名稱,以便我們可以快速解釋代碼中的時間表。

Crontab 語句有一個獨特的語法,涉及星號(或“星號”,如果您願意的話),其中每個星號代表某個時間單位。按順序,從左到右,星星代表:

  1. 分鐘
  2. 第二
  3. 小時
  4. 一個月中的哪一天
  5. 星期幾

正如我們在上面看到的,每顆星都可以用數字和字符代替,以指定特定的時間間隔。這是一個很大的話題,所以如果您對 crontab 本身的內部工作原理感到好奇,建議您閱讀本指南。

/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');
  });
}

回到我們的代碼中,現在我們已經準備好使用我們的 cron 作業了。就像我們之前看到的,我們從 /api/cron/scheduleConstants.js 導入命名的 crontab 語句 並將它們作為第一個參數傳遞給 cron.schedule() .

現在,我們已經準備好做一些實際的工作了……或者至少,一些 fake 工作。

在導出函數的上方和導入函數的下方,我們添加了一個函數 generateReport() 在某個時間間隔內模擬“生成報告”的工作。該函數接受任意 interval 名稱並嘗試在 reports 中創建文件 我們應用程序的目錄。每個文件的名稱採用 <interval>_<timestamp>.txt 的形式 <interval>interval 我們傳遞給 generateReport() 的名稱 函數和<timestamp> 是創建文件時的 ISO-8601 日期字符串標記。

要到達那裡,首先,我們確保 reports 目錄實際存在(需要,因為如果我們嘗試將文件寫入不存在的位置會出錯)。為此,我們在頂部導入了 fs 來自 fs 包——用於與文件系統交互的核心 Node.js 包。

從那個包中,我們使用 fs.existsSync() 查看 reports 是否 目錄存在。如果它沒有 ,我們繼續創建它。

如果它確實 存在,接下來,我們讀取目錄的當前內容(目錄內所有文件的數組列表)為existingReports 然後獲取該列表並通過 interval 過濾它 使用 JavaScript Array.filter 鍵入 功能。

有了所有這些,我們嘗試使用 <interval>_<timestamp>.txt 編寫我們的文件 我們在上面描述為文件名的模式,並將該文件的內容設置為讀取 Existing Reports: <count> 的字符串 <count> 等於 interval 的現有報告數 在生成時鍵入(例如,對於第一個報告,它是 0 , 下一個是 1 ,等等)。

而已!現在,當我們啟動我們的服務器時,我們應該看到我們的 cron 作業正在運行並且報告顯示在 /reports 中 目錄。

總結

在本教程中,我們學習瞭如何使用 node-cron 在 Node.js 中編寫和安排 cron 作業 包裹。我們學習瞭如何組織我們的 cron 作業代碼,並確保在我們的應用程序啟動後調用它。我們還了解了 crontab 語句的工作原理以及如何使用預先編寫的常量編寫多個 cron 作業,這使我們的 crontab 語句更易於理解。


Tutorial JavaScript 教程
  1. JSONP 指南

  2. 你應該知道這一點

  3. Promise.all 已解決

  4. 代碼和 Scrum 100 天的第 78 天:數字營銷服務、面試實踐和審查

  5. 使用 Javascript 和 Node.js 進行網頁抓取

  6. 如何在不使用 TypeScript 的情況下檢查 JavaScript 中的類型

  7. 如何將 Nuxt.js 與 Headless CMS 一起使用

  1. 為什麼點擊事件沒有觸發?

  2. AWS Lambda,CLI 方式(食譜)

  3. 瘋狂的想法:使用 Hooks 管理 React 狀態

  4. 為什麼是網頁腳本?

  5. 如何在 Swift 中將 Web 服務器嵌入到 React-Native 應用程序中

  6. 我需要寫困難的回文

  7. 如何在 Heroku 上的 Node.js 應用程序中調試內存洩漏

  1. 使用 Redis 和 Mongoose 構建緩存層

  2. 使用 AWS Cognito、API Gateway 玩 CORS

  3. 使用 Redux-Saga 處理副作用

  4. 如何構建 Nuxt 3 離子電容器啟動器應用程序