Надёжная отправка писем через SMTP в Node.js
Как отправлять транзакционные письма в фоне: очередь задач, SMTP-релей, защита от дублей и повторные попытки при сбоях.
На этой странице0%
Одобрение кампании, изменение баланса или уведомление о безопасности аккаунта не должны заставлять Node.js-запрос ждать ответа от почтового сервера. Если релей работает медленно, запрос может завершиться по тайм-ауту; если приложение повторит запрос, одно и то же сообщение может отправиться дважды. Для надёжной работы в production лучше записывать уведомление вместе с бизнес-событием, а затем отправлять его через SMTP в фоновом режиме.
Уберите отправку писем из обработки запросов
Представьте кампанию в криптовалютной рекламной сети, статус которой меняется с «На рассмотрении» на «Одобрена». Чтобы не терять время, приложение использует события вебхуков email и быстро фиксирует это изменение. Ответ рекламодателю не должен зависеть от того, успеет ли завершиться SMTP-подключение.
Создавайте задачу на отправку письма в таблице базы данных или надёжной очереди в рамках той же транзакции, что и изменение статуса. Воркер выбирает ожидающие задачи и отправляет письма через релей. Такая фоновая отправка писем в Node.js отделяет доступность приложения от доставки: временный сбой реле может задержать уведомление, но не отменит одобрение и не заблокирует действие в панели управления.
Если вы добавляете в существующий сервис SMTP-релей для Node.js и выясняете, как отправлять письма через SMTP в Node.js, ключевое решение — граница между приложением и воркером, а не выбор почтовой библиотеки. Платформа Email & messaging может предоставить SMTP-подключение для транзакционных сообщений, но приложению всё равно нужно определить, когда создавать сообщение, как выполнять повторные попытки и что делать, если результат доставки неизвестен.
Создавайте по одной задаче уведомления на каждое бизнес-событие
Создавайте задачу в момент фиксации исходного события, а не при каждой загрузке страницы или повторном запросе пользователя к API. Присвойте ей постоянный ключ события, например идентификатор кампании вместе с переходом статуса и версией. Обеспечьте уникальность этого ключа, чтобы повторные запросы не создавали несколько ожидающих писем об одобрении.
В полезную запись задачи входят получатель, идентификатор шаблона, минимальный набор данных для формирования сообщения, число попыток, время следующей попытки и статус. Не храните в ней учётные данные SMTP и лишнюю информацию об аккаунте. Если адрес получателя может измениться до отправки, заранее решите, использовать ли адрес, сохранённый на момент события, или текущий подтверждённый адрес аккаунта, и явно зафиксируйте это правило.
Формируйте письмо на основе реального изменения статуса. Воркер не должен определять факт одобрения по устаревшим данным из очереди. Перед отправкой важного уведомления он может проверить текущую версию события или загрузить соответствующую запись. Так клиент не получит сообщение об одобрении кампании, если сразу после этого её приостановили.
Настройте SMTP-релей Node.js в воркере
Используйте поддерживаемый SMTP-клиент, например Nodemailer, а параметры подключения считывайте из окружения воркера, не сохраняя их в исходном коде. В примере ниже используется распространённый порт отправки; настройте порт и параметры TLS в соответствии с учётными данными вашего аккаунта.
const nodemailer = require("nodemailer");
const port = Number(process.env.SMTP_PORT || 587);
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port,
secure: port === 465,
requireTLS: port !== 465,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
async function sendCampaignNotice(message) {
return transporter.sendMail({
from: process.env.MAIL_FROM,
to: message.to,
subject: message.subject,
text: message.text,
html: message.html
});
}
Укажите в поле отправителя адрес, одобренный для вашей конфигурации отправки, и используйте для воркера отдельные учётные данные, а не логин администратора. Не записывайте в логи сообщение целиком, пароль или ответ аутентификации. Проверка при запуске поможет обнаружить неверные настройки, но не гарантирует, что конкретный получатель примет последующее письмо.
Повторяйте попытки при временных сбоях, не создавая шквал писем
Классифицируйте ошибки, а не повторяйте попытки с одинаковой частотой при любых сбоях. Временную ошибку подключения или ответ сервера о высокой нагрузке можно обработать повторной попыткой с увеличивающимися интервалами. Неверные учётные данные, некорректный адрес получателя и другие постоянные ошибки конфигурации или адреса требуют проверки, а не частых повторов. Ограничьте число попыток и переводите задачи, исчерпавшие лимит, в статус, доступный для проверки оператором. Так формируется очередь транзакционных писем с повторными попытками, в которой временные сбои обрабатываются отдельно от постоянных ошибок.
Используйте аренду задачи воркером или аналогичный механизм блокировки, чтобы два процесса Node.js не отправили одну и ту же ожидающую задачу одновременно. При захвате задачи воркером сохраняйте срок действия аренды; если процесс завершится с ошибкой, другой воркер сможет забрать задачу после истечения этого срока. Устанавливайте срок аренды больше обычного тайм-аута отправки и продлевайте его, если формирование или доставка письма могут занять больше времени.
SMTP не может гарантировать для приложения строго однократную доставку. Релей может принять сообщение, а соединение — оборваться до того, как воркер получит подтверждение. Повторная попытка в таком случае может привести к дубликату; если же отметить сообщение как отправленное до подтверждения приёма, оно может потеряться. Используйте постоянные ключи событий, чтобы не создавать дубли задач, ограничивайте число повторных попыток и учитывайте, что эту неопределённость не всегда можно устранить автоматически.
Внедряйте изменения поэтапно и под контролем
Перед переключением production-трафика отправьте тестовые задачи из непроизводственной среды и проверьте, что воркер может пройти аутентификацию, сформировать ожидаемое содержимое и записать статусы успеха и ошибки. Затем включите воркер для одного типа уведомлений с низким уровнем риска. Сопоставьте число бизнес-событий, поставленных в очередь задач и завершённых отправок: растущая очередь может выявить проблему с воркером или настройками, прежде чем вы переведёте критически важные уведомления.
- Во время развёртывания используйте одну и ту же схему сообщений в веб-приложении и воркере.
- Обеспечьте идемпотентное создание задач до запуска нескольких экземпляров воркера.
- Отслеживайте возраст задач в очереди, число повторных попыток, постоянные ошибки и состояние воркеров.
- Предусмотрите ручной повторный запуск проверенных задач с ошибками, а не бездумную повторную отправку всех неудачных задач.
Когда работа воркера стабилизируется, удалите прямую отправку SMTP из обработчиков запросов, чтобы для таких сообщений остался единый контролируемый путь. Если вы используете платформу Email & messaging в качестве реле, она выполняет передачу по SMTP; очередь, правила обработки событий, политику повторных попыток и операционный контроль по-прежнему обеспечивает ваше приложение. Такое разделение полезно, когда события аккаунта нужно быстро фиксировать даже при кратковременных перебоях с почтой, но оно не отменяет необходимость мониторинга и чётких правил обработки задержанных уведомлений.