Запланированные задания Cron с рендерингом

Запланированные задания Cron с рендерингом

5 апреля 2022 г.

Программистам часто требуется запускать некоторые повторяющиеся процессы автоматически через фиксированные интервалы времени или в определенное время. Распространенным решением этой проблемы является использование задания cron. Когда у вас есть полный доступ к вашему собственному серверу, настройка заданий cron довольно проста. Однако насколько сложно настроить задания cron при использовании службы хостинга приложений? К счастью, некоторые сервисы предоставляют вам возможность сделать это.


В этой статье мы рассмотрим пример мини-проекта, в котором показано, как легко настроить и развернуть задание cron на [Render] (https://render.com/).


Основные концепции


Что такое задание Cron?


Задание cron — это команда Unix, которая запускается cron как фоновый процесс по расписанию, определяемому [выражением Cron] (https://en.wikipedia.org/wiki/Cron#CRON_expression). Как правило, cron определяет задания для запуска через файлы конфигурации crontab, которые состоят из пар выражений cron и соответствующих команд.


Что такое рендеринг?


Render — это служба облачного хостинга приложений, которая предлагает различные решения для хостинга веб-служб, такие как статические сайты, веб-серверы, базы данных и, да, даже задания cron! Render берет на себя заботы о хостинге и развертывании, поэтому вы можете тратить все свое время на создание своих проектов.


Что такое задачи рендеринга Cron?


Render предлагает услугу размещения заданий cron, которая упрощает процесс развертывания и обслуживания задания cron в облаке. Чтобы настроить службу заданий Render cron, просто свяжите репозиторий GitHub, выберите среду выполнения и укажите команду для запуска и выражение cron для определения расписания.


Обзор нашего мини-проекта


Наш проект будет простым сервисом, который позволит нам создавать и хранить заметки. Служба также запускает ежечасное задание cron, чтобы отправить нам по электронной почте все заметки, созданные за последний час. Приложение состоит из трех частей:


  • Веб-сервер Express, который обрабатывает запросы на создание заметок

  • База данных PostgreSQL для хранения заметок

  • Задание cron, которое отправляет электронное письмо с дайджестом заметок

Мы будем использовать службы рендеринга для каждого из этих компонентов. Мы также будем использовать [Mailjet] (https://www.mailjet.com/) в качестве службы для отправки электронных писем. Для нашего приложения Node.js мы добавим следующие пакеты зависимостей:


  • pg для взаимодействия с базой данных

  • express-async-handler в качестве обновления качества жизни, которое позволяет нам использовать асинхронные функции в качестве наших обработчиков Express.

  • node-mailjet, которая является официальной клиентской библиотекой, взаимодействующей с API Mailjet.

Предположим, что на вашем компьютере для разработки установлен Node.js. В нашем демонстрационном коде мы будем использовать Yarn для нашего менеджера пакетов.


Настройка репозитория проекта


Давайте начнем с настройки репозитория нашего проекта и нашего веб-сервиса на Render. Мы можем разветвить репозиторий Render Express Hello World для нашего исходного [шаблонного кода сервера Express] (https://github.com/render-examples/express-hello-world).


В Render мы создаем страницу веб-сервиса, которая использует разветвленное репо.


Описание изображения


Мы вводим имя для нашего веб-сервиса и продолжаем использовать все значения по умолчанию. После того, как Render завершит развертывание, мы увидим URL-адрес службы. Мы можем посетить этот URL-адрес в нашем браузере, чтобы убедиться, что все настроено правильно.


Теперь мы можем клонировать разветвленное репо на нашу машину разработки, а затем добавить наши зависимости:


``` ударить


~/project$ пряжа добавить pg экспресс-асинхронный-обработчик node-mailjet


После настройки исходного репозитория проекта давайте перейдем к настройке нашей базы данных.


Настройка базы данных


Наша база данных очень проста и состоит всего из одной таблицы с названием notes. В таблице будет столбец для хранения текста заметки и еще один столбец для хранения временной метки при создании заметки.


Мы создадим службу базы данных PostgreSQL на Render.


Описание изображения


Мы указываем имя службы базы данных, а затем используем значения по умолчанию для всех остальных параметров. После создания базы данных мы можем подключиться к ней с нашего локального компьютера и создать таблицу «notes». Скопируйте внешнюю строку подключения с панели управления базой данных, а затем запустите узел REPL в вашем локальный каталог проекта. Мы будем использовать пул соединений, чтобы сделать запрос к нашей базе данных, поэтому нам нужно импортировать класс Pool и создать Pool объект с нашей внешней строкой подключения:


```javascript


const { Пул } = требуется ('pg');


постоянный пул = новый пул (


{ connectionString: '<Внешняя строка подключения>?ssl=true'}


Обратите внимание, что поскольку мы подключаемся через SSL в REPL node, нам нужно добавить ?ssl=true в конец строки подключения. Создав наш объект пула, мы можем выполнить запрос для создания таблицы:


```javascript


пул.запрос(


'СОЗДАТЬ заметки TABLE (текстовый текст, временная метка создания);',


console.log


Вуаля! Наша база данных настроена с нашей таблицей notes!


Настройка группы окружения в Render


Прежде чем мы добавим в наш веб-сервис функциональность для начала заполнения таблицы, давайте удостоверимся, что наш веб-сервис имеет доступ к нашей базе данных. Фактически, поскольку и наша веб-служба, и задание cron должны будут подключаться к базе данных, мы можем воспользоваться преимуществами групп среды Render для создания общей группы переменных среды, которую мы можем использовать для обеих служб.


Для этого нам понадобится внутренняя строка подключения из панели управления базой данных, поскольку и веб-служба, и задание cron будут взаимодействовать с базой данных через внутреннюю сеть Render. Нажмите Env Groups на главной панели навигации Render.


Описание изображения


Затем нажмите Новая группа среды.


Описание изображения


Выберите имя для группы среды. Затем добавьте новую переменную с ключом CONNECTION_STRING и вставьте внутреннюю строку подключения в качестве значения (на этот раз нет необходимости в ssl=true).


Создав группу, вы можете вернуться к настройкам Среды для веб-службы. В разделе Связанные группы среды вы можете выбрать только что созданную группу среды и нажать Связать. Теперь наш код Node.js может получить доступ к любым переменным, которые мы определяем в этой группе, через глобальный объект process.env. Мы увидим пример этого, когда начнем создавать наше приложение Express. Давайте сделаем это сейчас!


Создание экспресс-приложения


Наше приложение Express будет иметь только одну конечную точку, /notes, где мы будем обрабатывать запросы POST и GET.


Когда мы получаем запрос POST, мы создаем новую строку заметки в базе данных. Мы ожидаем, что Content-Type запроса будет application/json, а тело будет отформатировано как {"note": "<текст примечания>"}. Мы также отметим время запроса и сохраним эту временную метку в качестве «созданного» значения заметки.


Когда мы получаем запрос GET, мы запрашиваем у базы данных все заметки и возвращаем их в виде ответа JSON.


Давайте начнем с того, что избавимся от всего ненужного кода из нашего шаблона. Нам нужно только сохранить следующие строки, и мы немного изменим обратный вызов app.listen:


```javascript


константный экспресс = требуется ('экспресс');


константное приложение = экспресс();


постоянный порт = process.env.PORT || 3001;


app.listen(port, () => console.log(Сервер Notes прослушивает порт ${port}!));


Далее, давайте добавим все импорты, которые нам понадобятся. Мы снова будем использовать соединение Pool для подключения к базе данных:


```javascript


const { Пул } = требуется ('pg');


Кроме того, мы будем использовать пакет «express-async-handler»:


```javascript


const asyncHandler = require('экспресс-асинхронный-обработчик');


Мы создаем экземпляр нашего пула с переменной окружения CONNECTION_STRING:


```javascript


const connectionString = process.env.CONNECTION_STRING;


постоянный пул = новый пул ({connectionString});


Поскольку мы ожидаем запрос JSON POST, давайте также используем промежуточное ПО JSON от Express, которое будет анализировать тело запроса в 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({примечания: result.rows});


Во-первых, мы регистрируем асинхронную функцию с помощью asyncHandler в конечной точке /notes, используя app.get. В теле обратного вызова мы хотим выбрать все заметки в базе данных с помощью pool.query. Мы возвращаем ответ JSON со всеми строками, которые мы получили из базы данных.


И это все, что нам нужно для обработчика GET!


На этом этапе мы можем зафиксировать и отправить эти изменения. Render автоматически создает и повторно развертывает наше обновленное приложение. Мы можем убедиться, что наш обработчик GET работает, но пока все, что мы видим, это грустный, пустой объект заметок.


Обработка запросов POST /notes


Давайте перейдем к нашему обработчику POST, чтобы мы могли начать заполнять нашу базу данных некоторыми заметками! Наш код выглядит так:


```javascript


app.post('/notes', asyncHandler(async (req, res) => {


постоянный запрос = {


text: 'ВСТАВИТЬ В примечания ЗНАЧЕНИЯ ($1, $2);',


значения: [req.body.note, новая дата()],


ждать pool.query(запрос);


res.sendStatus(200);


Во-первых, мы вставляем новую строку в нашу базу данных с нашим текстом заметки и отметкой времени создания. Мы получаем текст заметки из req.body.note и используем new Date() для получения текущего времени. Объект Date преобразуется в тип данных PostgreSQL с помощью [параметризованных запросов] (https://node-postgres.com/features/queries#parameterized-query). Мы отправляем запрос на вставку, а затем возвращаем ответ «200».


Развернуть и протестировать


После отправки нашего кода и повторного развертывания Render мы можем протестировать наш сервер, отправив несколько тестовых запросов. В командной строке мы используем curl:


``` ударить


curl -X POST <ВСТАВИТЬ URL-адрес веб-службы>/notes \


-H 'Тип содержимого: приложение/json' \


-d '{"примечание": "<ВСТАВИТЬ ТЕКСТ ПРИМЕЧАНИЯ>"}'


Затем вы можете посетить конечную точку /notes в своем браузере, чтобы увидеть все свои недавно созданные заметки!


Создание задания Cron


Последний компонент, который связывает наш проект воедино, — это задание cron. Это задание cron будет запускаться в начале каждого часа, отправляя нам по электронной почте все заметки, созданные за последний час.


Настроить Mailjet


Мы будем использовать Mailjet в качестве службы доставки электронной почты. Вы можете зарегистрировать бесплатную учетную запись [здесь] (https://app.mailjet.com/signup).


Вам понадобится ваш ключ API Mailjet и секретный ключ со страницы управления ключами API. Давайте добавим эти ключи в группу окружения, которую мы создали ранее. Добавьте следующие переменные среды:


  • MAILJET_APIKEY

  • MAILJET_SECRET

  • USER_NAME: имя получателя электронной почты (ваше имя)

  • USER_EMAIL: адрес электронной почты получателя (ваш адрес электронной почты)

Реализовать сценарий задания Cron


Теперь давайте напишем скрипт, который мы будем запускать как задание cron, которое мы можем назвать mail_latest_notes.js. Опять же, мы будем использовать «пул» для запроса нашей базы данных, и мы также хотим инициализировать наш клиент Mailjet с нашими переменными среды:


```javascript


const { Пул } = требуется ('pg');


const mailjet = требуется ('узел-mailjet')


.connect(process.env.MAILJET_APIKEY, process.env.MAILJET_SECRET);


const connectionString = process.env.CONNECTION_STRING;


постоянный пул = новый пул ({connectionString});


Далее давайте запросим в базе данных все заметки, созданные за последний час. Поскольку это будет асинхронная операция, мы можем обернуть остальную часть скрипта в асинхронный IIFE, что позволит нам использовать ключевое слово await, чтобы упростить работу с ним:


```javascript


(асинхронный () => {


// весь оставшийся код будет здесь


Мы используем другой параметризованный запрос с new Date(), чтобы получить текущее время и использовать его для фильтрации заметок. На этот раз, однако, мы хотим получить время на час раньше текущего времени, что мы можем сделать с помощью методов даты setHours и getHours, чтобы мы могли фильтровать все заметки после этой метки времени:


```javascript


постоянная отметка времени = новая дата();


timestamp.setHours(timestamp.getHours() - 1);


постоянный запрос = {


текст: 'ВЫБЕРИТЕ * ИЗ заметок, ГДЕ создан >= $1;',


значения: [отметка времени],


константный результат = ожидание pool.query(запрос);


Мы проверяем, сколько строк было возвращено, и не будем отправлять электронное письмо, если нет заметок для отправки.


```javascript


если (результат.строки.длина === 0) {


console.log('Нет последних заметок');


процесс.выход();


Если есть строки, то мы создаем сообщение электронной почты с извлеченными заметками. Мы извлекаем текст из каждой строки примечания с помощью map и используем HTML для простого форматирования, объединяя все тексты примечаний с помощью тегов <br>:


```javascript


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


Наконец, мы используем клиент Mailjet, чтобы [отправить электронное письмо] (https://dev.mailjet.com/email/guides/send-api-v31/#send-a-basic-email) с сообщением, которое мы только что создали, и переменные среды, которые мы установили ранее. Мы также можем зарегистрировать ответ, который мы получили от Mailjet, просто чтобы убедиться, что наше электронное письмо было отправлено:


```javascript


константа mailjetResponse = mailjet


.post('отправить', {'версия': 'v3.1'})


.запрос({


'Сообщения':[{


'От': {


«Электронная почта»: process.env.USER_EMAIL,


«Имя»: process.env.USER_NAME


'К': [{


«Электронная почта»: process.env.USER_EMAIL,


«Имя»: process.env.USER_NAME


«Тема»: «Последние заметки»,


'HTMLPart': <p>${emailMessage}</p>


console.log(mailjetResponse);


Это все, что нам нужно для нашего сценария!


Настроить службу заданий рендеринга Cron


Наконец, давайте создадим службу заданий cron на Render.


Описание изображения


Мы даем нашей службе заданий cron имя и устанавливаем среду «Node». Затем мы устанавливаем поле команды на node mail_latest_notes.js. Чтобы запускать скрипт каждый час, мы устанавливаем в поле расписания выражение cron 0 * * * *. Render имеет изящную метку под вводом, которая показывает, что означает выражение cron на простом английском языке. Мы создаем задание cron.


Затем мы переходим на вкладку Среда для службы заданий cron и связываем созданную ранее группу среды. Все, что осталось сделать, это дождаться, пока Render завершит создание нашего сервиса заданий cron. Тогда мы можем проверить это! Перед завершением сборки вы можете создать дополнительные заметки, чтобы убедиться, что скрипт отправляет электронное письмо. Наконец, вы можете нажать кнопку Trigger Run на панели управления cron, чтобы вручную запустить скрипт, и проверить свой почтовый ящик, чтобы убедиться, что вы получили это письмо.


На этом мы закончили наш проект заметок!


Заключение


Планировщики заданий, такие как cron, являются мощными инструментами, предоставляющими простой интерфейс для запуска автоматизированных процессов по строгому расписанию. Некоторые службы хостинга приложений, такие как Render, упрощают настройку служб заданий cron вместе с веб-службами и службами баз данных. В этой статье мы рассмотрели, как это сделать, создав мини-проект, который сохраняет заметки, а затем отправляет дайджест по электронной почте, запускаемый заданием cron каждый час. С Render координирование связи между нашими различными компонентами и настройка задания cron были простыми и понятными.


Удачного кодирования!


Также опубликовано [Здесь] (https://dzone.com/articles/scheduled-cron-jobs-with-render)



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE