Второстепенные рабочие в Blitz

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

Когда пользователь регистрируется для вашего приложения, и вы хотите отправить ему письмо с подтверждением. Вам действительно нужно дождаться отправки электронного письма, чтобы показать им страницу успешной регистрации? Что произойдет, если письмо не будет отправлено?

Фоновые работники - отличное решение таких проблем. Обычно фоновый рабочий процесс - это долго работающая служба, ожидающая появления работы в некоторой очереди. Когда ваша служба добавляет новую задачу в очередь, работник может завершить ее независимо от службы, поставившей ее в очередь. Это означает, что вместо того, чтобы ждать завершения HTTP-запроса к третьей стороне, все, что вам нужно сделать, это дождаться постановки задания в очередь в Redis. Если сторонняя служба не работает и возникает исключение, задание будет автоматически повторено, а не потеряно навсегда.

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

Независимо от того, что вы делаете, часто бывает приятно иметь возможность передать работу фоновому исполнителю и оставить ваш веб-сервер для обработки того, для чего он был предназначен (обслуживания ответов на веб-запросы!).

Хотите прочитать эту историю позже? Сохраните в Журнале.

С Blitz, использующим Javascript как для клиента, так и для сервера, имеет смысл использовать Javascript только для фоновых рабочих. Подумайте обо всем этом сладком повторном использовании кода.

BullMQ - одна из ведущих сред для фоновых рабочих в экосистеме Javascript, и именно ее мы будем использовать для запуска фоновых рабочих с Blitz.

В этой статье мы рассмотрим настройку очереди и воркера BullMQ для вашего приложения Blitz таким образом, чтобы вы могли повторно использовать весь существующий код в фоновых воркерах.

Для начала нам нужно добавить в проект BullMQ, что мы можем сделать с помощью пряжи следующим образом:

yarn add bullmq

Поскольку BullMQ требует длительных рабочих процессов, нам нужен способ объединить весь наш исходный код в сценарий, готовый к запуску с помощью Node. Для этого мы будем использовать Webpack, но ваш любимый сборщик тоже будет работать.

Теперь мы добавим Webpack и пару других пакетов, которые нам понадобятся в наш проект:

yarn add -D webpack webpack-cli webpack-node-externals ts-loader typescript dotenv

webpack и webpack-cli позволят нам запустить двоичный файл Webpack для создания нашего рабочего скрипта BullMQ.

webpack-node-externals позволяет нам легко указать Webpack игнорировать наш node_modules каталог. При запуске node нам не нужно связывать наш каталог node_modules, поскольку Node может разрешать модули там сам.

typescript и ts-loader позволяют Webpack компилировать и связывать машинописный текст, который есть в нашем проекте Blitz. Если вы не используете Typescript в своем проекте, то эти пакеты вам не понадобятся.

dotenv позволяет нам вводить любые переменные среды, которые потребуются работникам BullMQ, через .env файл.

Как именно вы структурируете все в своем репозитории Blitz, зависит от вас, но мы будем помещать все в каталог jobs, который Blitz создает для нас по умолчанию.

Внутри этого каталога мы создаем три подкаталога:

workers - организует всех наших рабочих
queues - организует все наши очереди и имена очередей
src - точка входа для нашего рабочего процесса

Начнем с создания нашей первой очереди для отправки асинхронных писем. Это пример того, как может выглядеть файл ourjobs/queues/index.js:

import { Queue } from "bullmq"
export const EMAIL_QUEUE_NAME = "sendemail"
export const queueConfig = {
  connection: { 
    port: parseInt(process.env.REDIS_PORT, 10), 
    host: process.env.REDIS_HOST 
  },
}
export const emailQueue = new Queue(EMAIL_QUEUE_NAME, queueConfig)

Импортируем класс Queue из библиотеки BullMQ. Мы используем константу для определения имени очереди, чтобы упростить обновление в будущем, если нам понадобится. Затем мы определяем параметры подключения Redis (хост и порт), полученные из переменных env. Наконец, мы создаем новую очередь с учетом имени и параметров подключения, определенных выше.

Мы экспортируем все эти элементы, потому что они понадобятся нам позже как в нашем приложении Blitz, так и в нашем рабочем процессе.

Работник электронной почты в jobs/workers/emailWorker.tsx также прямолинеен:

import { Worker } from "bullmq"
import { EMAIL_QUEUE_NAME, queueConfig } from "jobs/queues"
import { sendEmail } from "app/mailer"
const emailWorker = new Worker(
  EMAIL_QUEUE_NAME,
  async (job) => {
    return sendEmail(job.data)
  },
  queueConfig
)
export default emailWorker

Мы импортируем класс Worker из библиотеки BullMQ, имя нашей очереди электронной почты и конфигурацию из только что созданного файла очередей. Наконец, мы импортируем функцию-оболочку для фактической отправки электронной почты через API Sendgrid.

Затем мы создаем рабочий экземпляр, передавая имя очереди электронной почты, функцию, которую нужно запустить, и конфигурацию Redis, которую мы определили в нашем файле очередей.

В файле точки входа jobs/src/index.tsx все, что нам нужно сделать, это импортировать нашего воркера:

import { emailWorker } from "jobs/workers"

В оболочке почтовой программы Sendgrid мы можем поместить задание в очередь следующим образом:

import { emailQueue } from "jobs/queues"
emailQueue.add("sendgrid", options)

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

Теперь нам нужно настроить Webpack для объединения этого источника и запуска наших рабочих процессов в процессе Node. Мы можем создать файл конфигурации в нашем корневом каталоге Blitz с именем worker.webpack.config.js:

const path = require("path")
const nodeExternals = require("webpack-node-externals")
const projectRoot = path.resolve(__dirname)
module.exports = {
  target: "node",
  mode: process.env.NODE_ENV,
  context: projectRoot,
  entry: {
    worker: "./jobs/src/index.tsx",
  },
  resolve: {
    modules: ["node_modules", projectRoot],
    extensions: [".ts", ".tsx", ".js", ".json"],
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
        options: {
          configFile: "tsconfig.worker.json",
        },
      },
    ],
  },
}

Несколько замечаний по поводу конфигурации:

entry убедитесь, что это указывает на наш сценарий входа в рабочий. В конце концов, мы захотим разделить сотрудников на отдельные процессы, чтобы иметь возможность масштабировать их в соответствии с их индивидуальными потребностями. Мы можем легко добавить сюда несколько рабочих скриптов, которые будут объединены Webpack, когда нам понадобится эта функциональность.

externals - это массив, который нужно исключить из объединения непосредственно в выходной файл. Здесь мы можем использовать пакет webpack-node-externals, чтобы легко исключить каталог node_modules из пакета.

module здесь мы можем определить загрузчики и плагины для обработки файлов определенных типов. Мы используем ts-loader, чтобы включить поддержку Typescript. Конфигурационный файл tsconfig.worker.json показан здесь:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false
  }
}

Мы расширяем параметр Blitz / Next tsconfig.json по умолчанию, но нам нужно изменить параметр noEmit с true на false, потому что Next.js не нужно фактически генерировать файл Javascript.

Наконец, нам нужно настроить несколько новых сценариев npm в нашем package.json, чтобы собрать и запустить нашего нового воркера.

"build:worker": "yarn run webpack --config ./worker.webpack.config.js",
"start:worker": "node ./dist/worker.js",
"start:worker:dev": "node -r dotenv/config ./dist/worker.js"

Чтобы собрать воркера, мы определяем скриптbuild:worker, в котором мы запускаем Webpack, и передаем в наш специфичный для воркера файл конфигурации.

Чтобы запустить воркер, нам просто нужно запустить собранный файл с помощью Node. Мы можем добавить отдельный сценарий для запуска его в разработке, который использует пакет dotenv для загрузки переменных среды из файла .env.

На этом этапе мы должны быть в порядке!

Попробуйте создать рабочий сценарий с помощью yarn build:worker, а затем запустить его с помощью yarn start:worker:dev. После запуска сценария попробуйте поставить задание в очередь, запустив любой поток, который вы настроили для добавления задания в свою очередь. Работу должен взять и выполнить наш работник.

Надеюсь, вы нашли эту статью полезной для запуска фоновых рабочих процессов вместе с вашим приложением Blitz.

📝 Сохраните эту историю в Журнале.

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