При разработке портала передо мной возникла задача создания единого сервиса доставки сообщений от различных сервисов до пользователя. Если вы работаете над проектом, состоящим из одного сервиса - вам будет вполне достаточно средств ActionMailer. Однако если ваш проект состоит из нескольких сервисов - возникают сложности.
Задача
В моем случае задачу можно сформулировать следующим образом:
- Персональные данные пользователя (email, имя, все персональные настройки) хранятся в сервисе Паспорт
- Отправка сообщений пользователю должна осуществляться всеми сервисами портала, коих на данный момент 4
- Сервисы могут отправлять следующие типы сообщений:
- Разовые сообщения о системных событиях адресованные конкретному пользователю - регистрация, восстановление пароля, уведомление о блокировке и т.д.
- Периодические сообщения для группы пользователей - рассылки, подписка на комментарии и прочие
- Доставка сообщений может быть как по почте, так и через Jabber, ICQ, SMS и прочие
- Должна формироваться очередь отправки сообщений, позволяющая распределять загрузку по времени, чтобы не убивать канал отправки. Например, чтобы не попасть в spam-листы из-за большого количества исходящих сообщений.
Так как система состоит из отдельных сервисов, самым логичным будет выделить один сервис, отвечающий за доставку сообщений - Сервис сообщений, которому Паспорт будет сообщать обо всех изменениях в данных пользователя. Все сервисы должны формировать сообщение и отдавать его Сервису сообщений для доставки.
Сервис сообщений должен состоять из двух частей:
- Web-интерфейса для добавления сообщений в очередь, регистрации пользователей и подписок
- Скрипта или демона для отправки сообщений
HTTP-интерфейс
HTTP-интерфейс используется сервисами для выполнения операций с пользователями и сообщениями. Интерфейс должен обладать следующим набором функций:
Добавление, изменение и удаление пользователя
Очевидно, что для пользователя должны передаваться основные параметры, используемые при рассылке (полное имя, email, jabber и другие контактные данные), а так же дополнительные данные, которые могут быть использованы при формировании сообщений. Например, половая принадлежность, город проживания, дата рождения и прочие. Эти данные в Сервис сообщений могут передать различные сервисы. Например, паспорт может передать пол и возраст, а доска объявлений - количество добавленных пользователем объявлений. Данные пользователя привязываются к уникальному системному ID (в моем случае это ID пользователя в Паспорте)
Добавление пользователя в листы рассылки и удаление из них
Фактически, листами рассылки может являться что угодно - почтовая подписка на новые материалы, уведомления о комментариях, сообщения в community, системные рассылки и так далее. Детали о том, что это за рассылка и каким образом она формируется, Сервиса сообщений не касаются - он занимается исключительно отправкой. Поэтому единственное что ему нужно знать о рассылке - это ее уникальный ID. В качестве такового можно использовать, например, комбинацию из названия сервиса и внутреннего ID объекта, к которому привязана рассылка. Например, уведомления о комментариях к статье номер 123 -
publishing_article_123_comments, новые объявления раздела Недвижимость -classifieds_section_realty_ads.Отправка персонального сообщения
Сервис может отправить сообщение пользователю используя его уникальный системный ID. Сервису сообщений передается полный текст сообщения, при необходимости содержащий инструкции для вставки пользовательских данных, уже переданных другим сервисом (например, имени пользователя или его пола). Для обработки тела сообщения можно использовать обработчик шаблонов. Для этого хорошо подойдут Liquid и Radius. Обработка происходит непосредственно перед отправкой
Отправка сообщения для листа рассылки
Сервис может добавить сообщение для листа рассылки, используя его уникальный ID (см. выше). Добавленное сообщение прогоняется через обработчик шаблонов для каждого пользователя, подписанного на рассылку, и результирующее сообщение ставится в общую очередь отправки.
Получение списка рассылок, на которые подписан пользователь
Сервисы должны иметь возможность получить данные о том, на какие рассылки подписан пользователь. Данные используются для работы форм подписки на объекты в сервисах.
Скрипт рассылки
Чтобы разделить добавление и отправку сообщений, стоит вынести непосредственно рассылку в отдельный процесс. Запускать сервис можно как по крону (для небольших объемов сообщений), так в виде демона. Скрипт должен выполнять ряд функций, которые стоит вынести из HTTP-интерфейса:
Отправка сообщений
Скрипт должен пачками забирать сообщения из очереди, отмечать их как находящиеся на обработке, а затем поочередно их отправлять используя указанные средства доставки - email, Jabber, ICQ или другие. В случае успешной отправки сообщения отмечаются как отправленные, в случае неудачи результат тоже сохраняется. Объем пакетов должен регулироваться для каждого средства доставки индивидуально.
Формирование очереди рассылок
Сообщения для рассылок через HTTP-интерфейс только регистрируются, а непосредственно формирование текста сообщений индивидуально для каждого пользователя должно происходить в скрипте рассылки. Например, если сообщение было добавлено в рассылку с 3000 подписчиков, то формирование списка сообщений для отправки (с учетом обработки шаблонов для персонализации) может занять несколько десятков секунд, что явно не вписывается во временной лимит для HTTP-интерфейса.
Подключаемые средства отправки
Если мы говорим не о узкоспециализированном сервисе, то необходимо учесть возможность добавления средств отправки в виде подключаемых плагинов или библиотек.
Технические средства
Для реализации HTTP-интерфейса можно использовать Rails или один из альтернативных Ruby-фреймворков. Для скрипта рассылки - rake (для запуска через cron), BackgroundRB, delayed_job или rudeq.
Что думаете по этому поводу?
