掘金 后端 ( ) • 2024-05-08 23:40

github-nodecron.png

一、简介

1.1)先看看 cron 背景:

cron表达式最初是由Unix操作系统中的cron守护进程所使用的一种语法规则,用于设置定时任务。

cron守护进程是Unix系统中的一个后台进程,用于周期性地执行指定的命令或脚本。

它可以根据用户的需求,按照指定的时间间隔或时间点来执行任务,通常用于定时备份、清理日志、发送邮件等操作。

当然 windows 也有定时任务,但是与 linux 和我们要讲的 cron 一些区别,windows 中叫 任务计划程序(本地),通过 任务计划程序(本地) 可以添加任务。接下来我们将通过 node-cron 在 node 平台上做定时任务。

1.2)定时任务目标:

使用 Node.js 创建发送每天定时发送邮件:

邮件内容是: `6 点了能下班了吗?`

二、先熟悉一下 node-cron 的规则

┌────────────── second (可选)
│ ┌──────────── 分钟 (minute,0 - 59)
│ │ ┌────────── 小时 (hour,0 - 23)
│ │ │ ┌──────── 一个月中的第几天 (day of month,1 - 31)
│ │ │ │ ┌────── 月份 (month,1 - 12)
│ │ │ │ │ ┌──── 一个星期中星期几 (day of week,0 - 6) 注意:星期天为 0
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *

如果我们想要每天早上 7 点开始任务吗,那么我们的 cron 语句应该:

* 7 * * *

三、初始化项目并安装依赖

cd your_dir
pnpm init
pnpm add node-cron nodemailer typescript tsx
mkdir src && cd src
touch index.ts

本项目使用 typescript + esm, 因为不涉及前端内容,其实纯 Node.js 模块模式也是可以的。这里使用 esm 模块。意味着我们要使用 .js 文件结束导入 esm。 下面给出一 tsconfig 配置:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

四、安装 docker

安装 docker,推荐安装图形化软件,因为定时任务,管理使用图形化可能后期维护自己的项目,更加方便。 docker-download.png

4.1)安装 docker 桌面端

docker-desktop.png

当然喜欢 cli 的小伙伴也可以直接用,注意安装 cli 需要安装社区版本。

4.2)添加 Dockerfile 文件

FROM node:18-alpine

RUN npm install -g pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install

COPY . .

RUN pnpm run build

CMD [ "pnpm", "start" ]

这里我们使用 node:18-alpine 一个较小的版本管理,然后使用 pnpm 管理项目,注意pnpm下载慢的换镜像源。然后指向构建相关的任务,最后开始运行开始名程序命令。

4.3)添加 .dockerignore 文件

node_modules

防止 docker 在 copy 文件的时候将 node_modules 也复制进去了。

五、安装依赖

pnpm add nodemailer dotenv node-cron

pnpm add @types/node @types/node-cron @types/nodemailer rimraf tsx typescript

5.1) 编写 npm script 运行脚本

"scripts": {
    "dev": "tsx ./src/index.ts",
    "build": "rimraf dist & tsc --outDir ./dist",
    "start": "node ./dist/index.js",
    "docker:build": "docker build -t node-cron .",
    "docker:run": "docker run -itd node-cron"
  },

六、编写代码

6.1)nodemailer 简单封装

import type SMTPConnection from "nodemailer/lib/smtp-connection";

import nodemailer from "nodemailer";

type SendMailOptions = {
  host: string;
  port: number;
  auth: {
    user: string;
    pass: string;
  };

  to: string; // 接收者的邮箱地址
  subject: string; // 邮件主题
  html?: string; //也可以用html发送
};

export function sendMail(options: SendMailOptions) {
  let transporter = nodemailer.createTransport({
    host: options.host, // 发送者的邮箱厂商,支持列表:https://nodemailer.com/smtp/well-known/
    port: options.port, // SMTP 端口
    secureConnection: false, // SSL安全链接
    secure: true,
    requireTLS: true,
    auth: {
      //发送者的账户密码
      user: options.auth.user, //账户
      pass: options.auth.pass, //smtp授权码,到邮箱设置下获取
    },
  } as SMTPConnection.Options);

  let mailOptions = {
    from: `<${options.auth.user}>`, // 发送者昵称和地址
    to: options.to, // 接收者的邮箱地址
    subject: options.subject, // 邮件主题
    html: options.html, //也可以用html发送
  };

  //发送邮件
  return new Promise((resolve, reject) => {
    transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        reject(error);
        return console.log(error);
      }

      resolve(info);
    });
  });
}

6.2)程序入口

import cron from "node-cron";
import dotenv from 'dotenv'

import { sendMail } from "./nodemail.js";

dotenv.config()

const data = {
    host: "smtp.163.com",
    port: 465,
    user: process.env.user,
    pass: process.env.pass,
    to: process.env.to,
    subject: "早安~",
    html: "新的一天开始了,保持开心😄~",
  };

cron.schedule("* 7 * * *", () => {
    sendMail({
    host: data.host,
    port: data.port,
    auth: {
      user: process.env.user!,
      pass: process.env.pass!,
    },
    to: process.env.to!, // 接收者的邮箱地址
    subject: data.subject, // 邮件主题
    html: data.html, //也可以用html发送
  }).then((res: any) => {
    console.log(res.response)
  }).catch(error => {
    console.log(error)
  });
  
  console.log("定时服务已启动~")
});

6.3) 配置环境变量

本文以 163 邮箱为例,设置环境变量

user = "" # 邮箱用户名
pass = "" # 邮箱授权吗
to = "" # 将要对象

当然,对于敏感信息,我们不能够

七、docker 构建与运行

7.1) 构建

因为有了 npm 脚本运行 docker 构建任务,我们使用

pnpm run docker:build

很简单使用 dockerfile 文件内容构建一个 node-cron 的镜像。有了镜像

node-cron构建镜像.png

7.2) 运行

运行容器.png

pnpm run docker:run # 将会会在后台运行

有了 docker 部署到自己的服务器也是非常的简单容易,这里就在做过多的介绍了。

八、其他适合 cron 的任务

  • 定时数据库备份
  • 定时充值清理数据
  • 定时构建
  • 定时签到等
  • ...

九、小结

本文主要讲解 node-cron 相关内容,定时任务非常有用,能够帮助我们做一些重复的内容,实现一些自动化。node-cron 是 node.js 平台的 cron, 使用 node-cron + docker 部署项目能够帮助我们方便的发送定时邮件、以及其他的任务。希望能够帮助读者。