使用Render的预定Cron作业

506 阅读9分钟

DZone>Web Dev Zone>使用Render的预定Cron作业

使用Render调度的Cron工作

使用云主机平台的开发者可能也需要调度后台任务的Cron作业。本教程将介绍如何使用Render来完成这一切。

Alvin Lee user avatar作者

阿尔文-李

CORE -

Mar. 28, 22 - Web Dev Zone -教程

喜欢 (2)

评论

保存

鸣叫

2.00K浏览次数

加入DZone社区,获得完整的会员体验。

免费加入

程序员经常需要在固定的时间间隔或特定的时间自动运行一些经常性的进程。这个问题的一个常见解决方案是使用cron job。当你可以完全访问你自己的服务器时,配置cron job是非常简单的。然而,当你使用一个应用程序托管服务时,配置cron job有多难呢?值得庆幸的是,有些服务为你提供了一种方法。

在这篇文章中,我们将通过一个小型项目样本,展示如何在Render上轻松设置和部署一个cron job。

核心概念

什么是Cron作业?

cron作业是一个Unix命令,cron ,作为一个后台进程,按照Cron表达式确定的时间表运行。一般来说,cron通过crontab配置文件来决定要运行的工作,这些文件由一对cron表达式和相应的命令组成。

什么是Render?

Render是一个云应用托管服务,它提供各种网络服务托管解决方案,如静态网站、网络服务器、数据库,是的,甚至还有cron作业!Render提供的是一个云应用托管服务。Render为你处理托管和部署的麻烦,这样你就可以把所有的时间都放在建立你的项目上。

什么是Render Cron作业?

Render提供了一个cron job托管服务,简化了在云端部署和维护cron job的过程。要设置一个Render cron job服务,只需链接一个GitHub repo,选择一个运行时间,并提供要运行的命令和cron表达式来确定时间表。

我们的小型项目概述

我们的项目将是一个简单的服务,让我们创建和存储笔记。该服务也会每小时运行一个cron作业,将过去一小时内创建的所有笔记通过电子邮件发给我们。该应用程序由三部分组成。

  1. 一个Express网络服务器,处理创建笔记的请求
  2. 一个PostgreSQL数据库来存储笔记
  3. 一个发送笔记摘要邮件的cron工作

我们将为这些组件中的每一个使用Render服务。我们还将使用Mailjet作为发送电子邮件的服务。对于我们的Node.js应用程序,我们将添加以下依赖包。

  • pg 来与数据库进行交互
  • express-async-handler 作为生活质量的升级,使我们能够使用异步函数作为我们的Express处理程序
  • node-mailjet,这是与Mailjet API互动的官方客户端库。

我们将假设你的开发机器上安装了Node.js。在我们的演示代码中,我们将使用Yarn作为我们的包管理器。

设置项目Repo

让我们先在Render上设置我们的项目仓库和网络服务。我们可以从Render的Express Hello World repo中获取我们最初的Express服务器模板代码。

在Render中,我们创建一个web服务页面,使用分叉的 repo。

我们为我们的网络服务输入一个名字,并使用所有的默认值进行。在Render完成部署后,我们会看到一个服务URL。我们可以在我们的浏览器中访问这个URL,以验证一切设置是否正确。

现在,我们可以把分叉的 repo 克隆到我们的开发机器上,然后添加我们的依赖项。

~/project$ yarn add pg express-async-handler node-mailjet

有了最初的项目 repo 设置,让我们继续设置我们的数据库。

设置数据库

我们的数据库非常简单,只由一个叫做notes的表组成。该表将有一列用于存储笔记文本,另一列用于存储创建笔记的时间戳。

我们将在Render上创建一个PostgreSQL数据库服务

我们为数据库服务提供一个名称,然后对所有其他选项使用默认值。创建数据库后,我们可以从本地机器连接到它,并创建notes 表。从数据库仪表板上复制外部连接字符串,然后在你的本地项目目录下启动node REPL。我们将使用一个连接池来对数据库进行查询,所以我们需要导入Pool 类,并用我们的外部连接字符串创建一个Pool 对象。

JavaScript

const { Pool } = require('pg');
const pool = new Pool(
  { connectionString: '<External Connection String>?ssl=true'}
);

注意,由于我们在node REPL中通过SSL连接,我们需要在连接字符串的末尾加上?ssl=true 。创建了我们的池对象后,我们可以执行查询来创建表。

JavaScript

pool.query(
  'CREATE TABLE notes (text text, created timestamp);',
  console.log
);

看吧!我们的数据库已经设置了我们的notes 表!

在Render中设置一个环境组

在我们为我们的网络服务添加功能以开始填充表之前,让我们确保我们的网络服务可以访问我们的数据库。事实上,因为我们的网络服务和cron job需要连接到数据库,我们可以利用Render的环境组来创建一个共享的环境变量组,我们可以为这两个服务使用。

要做到这一点,我们将需要来自数据库仪表板的内部连接字符串,因为网络服务和cron工作都将通过Render的内部网络与数据库进行通信。点击Render主导航中的环境组

接下来,点击新建环境组

为你的环境组选择一个名称。然后,添加一个新的变量,键值为CONNECTION_STRING ,并粘贴内部连接字符串作为值(这次不需要ssl=true )。

一旦你创建了这个组,你就可以回到网络服务的环境设置中。在链接环境组部分,你可以选择你刚刚创建的环境组,然后点击链接。现在,我们的Node.js代码可以通过全局process.env 对象访问我们在这个组中定义的任何变量。当我们开始建立我们的Express应用程序时,我们会看到一个这样的例子。让我们现在就去做吧!

创建Express应用程序

我们的 Express 应用程序将只有一个端点,即/notes ,我们将在这里处理POSTGET 请求。

当我们收到一个POST 请求时,我们会在数据库中创建一个新的笔记行。我们希望请求的Content-Typeapplication/json ,而正文的格式是{"note": "<note text>"} 。我们还将注意到请求的时间,并将该时间戳作为笔记的created

当我们收到一个GET 的请求时,我们将查询数据库中的所有笔记,并以JSON响应的形式返回。

让我们先从模板中删除所有不必要的代码。我们只需要保留以下几行,并且稍微改变一下app.listen 的回调。

JavaScript

const express = require('express');
const app = express();
const port = process.env.PORT || 3001;

app.listen(port, () => console.log(`Notes server listening on port ${port}!`));

接下来,让我们添加所有我们需要的导入。我们将再次使用一个连接Pool 来连接到数据库。

JavaScript

const { Pool } = require('pg');

此外,我们将使用express-async-handler 包。

JavaScript

const asyncHandler = require('express-async-handler');

我们用CONNECTION_STRING 环境变量来实例化我们的Pool

JavaScript

const connectionString = process.env.CONNECTION_STRING;
const pool = new Pool({connectionString});

由于我们期待一个JSONPOST 请求,让我们也使用Express的JSON中间件,它将把请求体解析成一个JavaScript对象,我们可以在req.body

JavaScript

app.use(express.json());

处理GET /notes 请求

现在我们可以进入我们应用程序的核心部分了:请求处理程序。我们将从我们的GET 处理程序开始,因为它比较简单。让我们先展示代码,然后解释我们做了什么。

JavaScript

app.get('/notes', asyncHandler(async (req, res) => {
  const result = await pool.query('SELECT * FROM notes;');
  res.json({ notes: result.rows });
}));

首先,我们使用app.get ,在/notes 端点注册一个异步函数asyncHandler 。在回调的主体中,我们要使用pool.query ,选择数据库中的所有笔记。我们返回一个JSON响应,其中包含我们从数据库收到的所有行。

这就是我们对GET 处理程序的全部需要!

在这一点上,我们可以提交和推送这些变化。Render会自动构建并重新部署我们更新的应用程序。我们可以验证我们的GET 处理程序是否工作,但现在,我们看到的只是一个悲伤的、空的笔记对象。

处理POST /notes 请求

让我们继续我们的POST 处理程序,这样我们就可以开始用一些笔记来填充我们的数据库了。我们的代码看起来像这样。

JavaScript

app.post('/notes', asyncHandler(async (req, res) => {
  const query = {
    text: 'INSERT INTO notes VALUES ($1, $2);',
    values: [req.body.note, new Date()],
  };
  await pool.query(query);
  res.sendStatus(200);
}));

首先,我们在数据库中插入一条新的记录,包含我们的笔记文本和创建时间戳。我们从req.body.note ,我们使用new Date() ,以获得当前时间。通过使用参数化查询Date 对象被转换为PostgreSQL的数据类型。我们发送插入查询,然后我们返回一个200 响应。

部署和测试

在推送我们的代码并让Render重新部署后,我们可以通过发送一些测试请求来测试我们的服务器。在命令行中,我们使用curl

Shell

curl -X POST <INSERT WEB SERVICE URL>/notes \
     -H 'Content-Type: application/json' \
     -d '{"note": "<INSERT NOTE TEXT>"}'

然后,你可以在你的浏览器中访问/notes 端点,以查看所有新创建的笔记。

创建Cron Job

将我们的项目联系在一起的最后一个组件是cron job。这个cron工作将在每小时的顶部运行,向我们发送过去一小时内创建的所有笔记的电子邮件。

设置Mailjet

我们将使用Mailjet作为我们的电子邮件发送服务。你可以在这里注册一个免费账户。

你将需要你的Mailjet API密钥和秘密密钥,从API密钥管理页面。让我们把这些密钥添加到我们先前创建的环境组中。添加以下环境变量。

  • MAILJET_APIKEY
  • MAILJET_SECRET
  • USER_NAME:电子邮件收件人的名字(你的名字)
  • USER_EMAIL: 收件人的电子邮件地址(你的电子邮件地址)

实施Cron Job脚本

现在让我们写一下我们将作为cron job运行的脚本,我们可以把它叫做mail_latest_notes.js 。同样,我们将使用Pool 来查询我们的数据库,而且我们还想用环境变量来初始化我们的Mailjet客户端。

JavaScript

const { Pool } = require('pg');
const mailjet = require ('node-mailjet')
  .connect(process.env.MAILJET_APIKEY, process.env.MAILJET_SECRET);
const connectionString = process.env.CONNECTION_STRING;
const pool = new Pool({connectionString});

接下来,让我们查询数据库中过去一小时内创建的所有笔记。由于这将是一个异步操作,我们可以将脚本的其余部分包裹在一个异步IIFE中,这将使我们能够使用await 关键字,使其更容易操作。

JavaScript

(async () => {
  // all remaining code will go here
})();

我们使用另一个带有new Date() 的参数化查询来捕获当前时间,并使用它来过滤笔记。但这次,我们要获得当前时间前一小时的时间,我们可以使用setHoursgetHours 日期方法来实现,这样我们就可以过滤出该时间戳之后的所有笔记。

JavaScript

const timestamp = new Date();
timestamp.setHours(timestamp.getHours() - 1);
const query = {
  text: 'SELECT * FROM notes WHERE created >= $1;',
  values: [timestamp],
};
const result = await pool.query(query);

我们检查有多少行被返回,如果没有任何笔记需要发送,我们就不会发送邮件。

脚本

if (result.rows.length === 0) {
  console.log('No latest notes');
  process.exit();
}

如果有行,我们就用检索到的笔记创建电子邮件。我们用map ,从每个笔记行中抽出文本,并使用HTML进行一些简单的格式化,用<br> 标签连接所有的笔记文本。

JavaScript

const emailMessage = result.rows.map(note => note.text).join('<br>');

最后,我们使用Mailjet客户端发送一封电子邮件,其中包含我们刚刚创建的消息和我们先前设置的环境变量。我们还可以记录从Mailjet得到的响应,只是为了确保我们的邮件被发送。

JavaScript

const mailjetResponse = mailjet
  .post('send', {'version': 'v3.1'})
  .request({
    'Messages':[{
      'From': {
        'Email': process.env.USER_EMAIL,
        'Name': process.env.USER_NAME
      },
      'To': [{
        'Email': process.env.USER_EMAIL,
        'Name': process.env.USER_NAME
      }],
      'Subject': 'Latest Notes',
      'HTMLPart': `<p>${emailMessage}</p>`
    }]
  });

console.log(mailjetResponse);

这就是我们的脚本所需要的全部内容!

设置Render Cron Job服务

最后,让我们在Render上创建cron job服务。

我们给我们的cron job服务起一个名字,并将环境设置为Node 。然后,我们将命令字段设置为node mail_latest_notes.js 。为了每小时运行脚本,我们将时间表字段设置为cron表达式0 * * * * 。Render在输入项下有一个漂亮的标签,显示了cron表达式的纯英文翻译。我们创建了这个cron job。

接下来,我们转到cron job服务的环境选项卡,并链接我们先前创建的环境组。剩下的就是等待Render完成建立我们的cron job服务。然后,我们就可以测试它了在构建完成之前,你可以创建更多的注释,以确保脚本会发送电子邮件。最后,你可以点击cron仪表盘上的Trigger Run按钮来手动运行脚本,并检查你的收件箱以确保你收到那封邮件。

就这样,我们完成了我们的笔记项目!

结论

cron 这样的工作调度器是强大的工具,它提供了一个简单的界面,可以按照严格的时间表运行自动化进程。一些应用程序托管服务--如Render--使你很容易在你的网络和数据库服务旁边设置cron job服务。在这篇文章中,我们通过建立一个小型项目,保存笔记,然后每小时发送由cron job触发的电子邮件摘要,来了解如何做到这一点。有了Render,协调我们不同组件之间的通信和设置cron job是直接和简单的。

编码愉快!

主题。

web开发, node.js, cron, 主机, express, render, 计划任务, 教程, cron job

DZone贡献者所表达的观点属于他们自己。

DZone上的热门话题


评论

网络开发 合作伙伴资源