Я разрабатываю веб-приложение и мне интересно, как спроектировать архитектуру для управления отправкой автоматических электронных писем.
В настоящее время у меня есть эта функция, встроенная в мое веб-приложение, и электронные письма отправляются на основе ввода / взаимодействия с пользователем (например, создание нового пользователя). Проблема в том, что подключение к почтовому серверу напрямую занимает пару секунд. Расширение моего приложения, это будет существенным препятствием в будущем.
Каков наилучший способ управления отправкой большого количества автоматических электронных писем в архитектуре моей системы?
Не будет отправлено огромное количество писем (максимум 2000 в день). Письма не нужно отправлять сразу, задержка до 10 минут - это нормально.
Обновление: Очередь сообщений была дана как ответ, но как это было бы разработано? Будет ли это обрабатываться в приложении и обрабатываться в течение тихого периода, или мне нужно создать новое «почтовое приложение» или веб-сервис, чтобы просто управлять очередью?
Ответы:
Общий подход, как уже упоминал Оз , - это очередь сообщений . С точки зрения дизайна очередь сообщений - это, по существу, очередь FIFO , которая является довольно фундаментальным типом данных:
Особенность очереди сообщений заключается в том, что, хотя ваше приложение отвечает за постановку в очередь, за удаление очереди будет отвечать другой процесс. В очереди на очереди ваше приложение является отправителем сообщения, а процесс удаления из очереди - получателем. Очевидным преимуществом является то, что весь процесс является асинхронным, получатель работает независимо от отправителя, пока есть сообщения для обработки. Очевидным недостатком является то, что вам нужен дополнительный компонент, отправитель, чтобы все это работало.
Поскольку ваша архитектура теперь основана на двух компонентах, обменивающихся сообщениями, вы можете использовать для этого причудливый термин межпроцессное взаимодействие .
Как введение очереди влияет на дизайн вашего приложения?
Определенные действия в вашем приложении генерируют электронные письма. Введение очереди сообщений будет означать, что эти действия теперь должны отправлять сообщения в очередь вместо этого (и ничего более). Эти сообщения должны содержать абсолютный минимальный объем информации, необходимый для построения электронных писем, когда ваш получатель получает их обрабатывать.
Формат и содержание сообщений
Формат и содержание ваших сообщений полностью зависит от вас, но вы должны помнить, что чем меньше, тем лучше. Ваша очередь должна быть настолько быстрой для записи и обработки, насколько это возможно, выброс больших объемов данных в нее, вероятно, создаст узкое место.
Кроме того, некоторые облачные службы очередей имеют ограничения по размеру сообщений и могут разбивать большие сообщения. Вы не заметите, что разделенные сообщения будут обрабатываться как единое целое, когда вы их просите, но вы будете платить за несколько сообщений (при условии, конечно, что вы используете услугу, которая требует платы).
Дизайн ресивера
Поскольку мы говорим о веб-приложении, общим подходом для вашего получателя будет простой скрипт cron. Он будет запускаться каждые
x
минуты (или секунды) и будет:n
количество сообщений из очереди,Обратите внимание, что я говорю pop вместо get или fetch, потому что ваш получатель не просто получает элементы из очереди, он также очищает их (т.е. удаляет их из очереди или помечает как обработанные). Как именно это произойдет, зависит от вашей реализации очереди сообщений и конкретных потребностей вашего приложения.
Конечно, я описываю, по сути, пакетную операцию , самый простой способ обработки очереди. В зависимости от ваших потребностей вы можете захотеть обрабатывать сообщения более сложным способом (это также потребует более сложной очереди).
Движение
Ваш получатель может принимать во внимание трафик и регулировать количество сообщений, которые он обрабатывает, основываясь на трафике во время его выполнения. Упрощенным подходом было бы предсказать ваши часы с большим трафиком на основе прошлых данных о трафике и предполагая, что вы использовали скрипт cron, который запускается каждую
x
минуту, вы можете сделать что-то вроде этого:Очень наивный и грязный подход, но он работает. Если этого не произойдет, то другим подходом будет выяснить текущий трафик вашего сервера на каждой итерации и соответствующим образом скорректировать количество элементов процесса. Пожалуйста, не оптимизируйте микро, если это не является абсолютно необходимым, хотя вы бы напрасно тратили время.
Хранение в очереди
Если ваше приложение уже использует базу данных, то единственная таблица в ней будет самым простым решением:
Это действительно не сложнее, чем это. Конечно, вы можете сделать это настолько сложным, насколько вам нужно, например, вы можете добавить поле приоритета (что будет означать, что это больше не очередь FIFO, но если вам это действительно нужно, кого это волнует?). Вы также можете упростить это, пропустив обработанное поле (но тогда вам придется удалять строки после того, как вы их обработали).
Таблица базы данных была бы идеальной для 2000 сообщений в день, но, вероятно, не будет хорошо масштабироваться для миллионов сообщений в день. Необходимо учитывать миллион факторов, и все в вашей инфраструктуре играет роль в общей масштабируемости вашего приложения.
В любом случае, если вы уже определили очередь на основе базы данных как узкое место, следующим шагом будет рассмотрение облачной службы. Amazon SQS - это единственная служба, которую я использовал и выполнил то, что обещал. Я уверен, что есть довольно много подобных услуг там.
Очереди на основе памяти - это тоже кое-что, особенно для коротких очередей. memcached отлично подходит для хранения очереди сообщений.
На каком бы хранилище вы ни решили построить свою очередь, будьте умны и абстрагируйтесь. Ни ваш отправитель, ни ваш получатель не должны быть привязаны к определенному хранилищу, в противном случае переключение на другое хранилище в более позднее время будет полным PITA.
Подход реальной жизни
Я построил очередь сообщений для писем, которая очень похожа на то, что вы делаете. Он был в проекте PHP, и я построил его вокруг Zend Queue , компонента Zend Framework, который предлагает несколько адаптеров для разных хранилищ. Мои хранилища где:
Мои сообщения были настолько просты, насколько это возможно, мое приложение создавало небольшие массивы с необходимой информацией (
[user_id, reason]
). Хранилище сообщений было сериализованной версией этого массива (сначала это был внутренний формат сериализации PHP, затем JSON, я не помню, почему я переключился). Этоreason
константа, и, конечно, у меня где-то есть большая таблица, которая отображаетreason
более полные объяснения (мне удалось разослать около 500 электронных писем клиентам с загадочным текстомreason
вместо одного более полного сообщения).дальнейшее чтение
Стандарты:
Инструменты:
Интересно читает:
источник
Вам нужна какая-то система очередей.
Одним простым способом может быть запись в таблицу базы данных и добавление в эту таблицу других строк процессов внешнего приложения, но есть много других технологий организации очередей, которые вы можете использовать.
Вы можете иметь значение для электронных писем, чтобы некоторые из них действовали почти немедленно (например, сброс пароля), а те, которые имеют меньшее значение, могли быть сгруппированы для последующей отправки.
источник
В дополнение к очереди, вторая вещь, которую вы должны рассмотреть, это отправка электронных писем через специализированные службы: например, MailChimp (я не связан с этой службой). В противном случае многие почтовые сервисы, такие как gmail, скоро отправят ваши письма в папку для спама.
источник
Я смоделировал мою систему очередей в разных 2 таблицах как;
Там 1-1 отношение между этими таблицами.
Таблица сообщений для хранения содержимого сообщения. Фактическое содержимое (To, CC, BCC, Subject, Body и т. Д.) Сериализуется в поле Graph в формате XML. Другое From, To информация просто используется для сообщения о проблемах без десериализации графа. Разделение этой таблицы позволяет разбить содержимое таблицы на другое дисковое хранилище. Когда вы готовы отправить сообщение, вам нужно прочитать всю информацию, поэтому нет ничего плохого в сериализации всего контента в один столбец с индексом первичного ключа.
Таблица MessageState для хранения состояния содержимого сообщения с дополнительной информацией на основе даты. Разделение этой таблицы позволяет использовать механизм быстрого доступа с дополнительными индексами для быстрого ввода-вывода. Другие столбцы уже говорят сами за себя.
Вы можете использовать отдельный пул потоков, который сканирует эти таблицы. Если приложение и пул находятся на одной и той же машине, вы можете использовать класс EventWaitHandle, чтобы сообщить пулу из приложения о том, что эти таблицы вставлены, в противном случае периодическое сканирование с таймаутом является лучшим.
источник