Общий подход, как уже упоминал Оз , - это очередь сообщений . С точки зрения дизайна очередь сообщений - это, по существу, очередь FIFO , которая является довольно фундаментальным типом данных:
Особенность очереди сообщений заключается в том, что, хотя ваше приложение отвечает за постановку в очередь, за удаление очереди будет отвечать другой процесс. В очереди на очереди ваше приложение является отправителем сообщения, а процесс удаления из очереди - получателем. Очевидным преимуществом является то, что весь процесс является асинхронным, получатель работает независимо от отправителя, пока есть сообщения для обработки. Очевидным недостатком является то, что вам нужен дополнительный компонент, отправитель, чтобы все это работало.
Поскольку ваша архитектура теперь основана на двух компонентах, обменивающихся сообщениями, вы можете использовать для этого причудливый термин межпроцессное взаимодействие .
Как введение очереди влияет на дизайн вашего приложения?
Определенные действия в вашем приложении генерируют электронные письма. Введение очереди сообщений будет означать, что эти действия теперь должны отправлять сообщения в очередь вместо этого (и ничего более). Эти сообщения должны содержать абсолютный минимальный объем информации, необходимый для построения электронных писем, когда ваш получатель получает их обрабатывать.
Формат и содержание сообщений
Формат и содержание ваших сообщений полностью зависит от вас, но вы должны помнить, что чем меньше, тем лучше. Ваша очередь должна быть настолько быстрой для записи и обработки, насколько это возможно, выброс больших объемов данных в нее, вероятно, создаст узкое место.
Кроме того, некоторые облачные службы очередей имеют ограничения по размеру сообщений и могут разбивать большие сообщения. Вы не заметите, что разделенные сообщения будут обрабатываться как единое целое, когда вы их просите, но вы будете платить за несколько сообщений (при условии, конечно, что вы используете услугу, которая требует платы).
Дизайн ресивера
Поскольку мы говорим о веб-приложении, общим подходом для вашего получателя будет простой скрипт cron. Он будет запускаться каждые x
минуты (или секунды) и будет:
- Pop
n
количество сообщений из очереди,
- Обрабатывать сообщения (т.е. отправлять электронные письма).
Обратите внимание, что я говорю pop вместо get или fetch, потому что ваш получатель не просто получает элементы из очереди, он также очищает их (т.е. удаляет их из очереди или помечает как обработанные). Как именно это произойдет, зависит от вашей реализации очереди сообщений и конкретных потребностей вашего приложения.
Конечно, я описываю, по сути, пакетную операцию , самый простой способ обработки очереди. В зависимости от ваших потребностей вы можете захотеть обрабатывать сообщения более сложным способом (это также потребует более сложной очереди).
Движение
Ваш получатель может принимать во внимание трафик и регулировать количество сообщений, которые он обрабатывает, основываясь на трафике во время его выполнения. Упрощенным подходом было бы предсказать ваши часы с большим трафиком на основе прошлых данных о трафике и предполагая, что вы использовали скрипт cron, который запускается каждую x
минуту, вы можете сделать что-то вроде этого:
if(
now() > 2pm && now() < 7pm
) {
process(10);
} else {
process(100);
}
function process(count) {
for(i=0; i<=count; i++) {
message = dequeue();
mail(message)
}
}
Очень наивный и грязный подход, но он работает. Если этого не произойдет, то другим подходом будет выяснить текущий трафик вашего сервера на каждой итерации и соответствующим образом скорректировать количество элементов процесса. Пожалуйста, не оптимизируйте микро, если это не является абсолютно необходимым, хотя вы бы напрасно тратили время.
Хранение в очереди
Если ваше приложение уже использует базу данных, то единственная таблица в ней будет самым простым решением:
CREATE TABLE message_queue (
id int(11) NOT NULL AUTO_INCREMENT,
timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed enum('0','1') NOT NULL DEFAULT '0',
message varchar(255) NOT NULL,
PRIMARY KEY (id),
KEY timestamp (timestamp),
KEY processed (processed)
)
Это действительно не сложнее, чем это. Конечно, вы можете сделать это настолько сложным, насколько вам нужно, например, вы можете добавить поле приоритета (что будет означать, что это больше не очередь FIFO, но если вам это действительно нужно, кого это волнует?). Вы также можете упростить это, пропустив обработанное поле (но тогда вам придется удалять строки после того, как вы их обработали).
Таблица базы данных была бы идеальной для 2000 сообщений в день, но, вероятно, не будет хорошо масштабироваться для миллионов сообщений в день. Необходимо учитывать миллион факторов, и все в вашей инфраструктуре играет роль в общей масштабируемости вашего приложения.
В любом случае, если вы уже определили очередь на основе базы данных как узкое место, следующим шагом будет рассмотрение облачной службы. Amazon SQS - это единственная служба, которую я использовал и выполнил то, что обещал. Я уверен, что есть довольно много подобных услуг там.
Очереди на основе памяти - это тоже кое-что, особенно для коротких очередей. memcached отлично подходит для хранения очереди сообщений.
На каком бы хранилище вы ни решили построить свою очередь, будьте умны и абстрагируйтесь. Ни ваш отправитель, ни ваш получатель не должны быть привязаны к определенному хранилищу, в противном случае переключение на другое хранилище в более позднее время будет полным PITA.
Подход реальной жизни
Я построил очередь сообщений для писем, которая очень похожа на то, что вы делаете. Он был в проекте PHP, и я построил его вокруг Zend Queue , компонента Zend Framework, который предлагает несколько адаптеров для разных хранилищ. Мои хранилища где:
- PHP массивы для модульного тестирования,
- Amazon SQS на производстве,
- MySQL в среде разработки и тестирования.
Мои сообщения были настолько просты, насколько это возможно, мое приложение создавало небольшие массивы с необходимой информацией ( [user_id, reason]
). Хранилище сообщений было сериализованной версией этого массива (сначала это был внутренний формат сериализации PHP, затем JSON, я не помню, почему я переключился). Это reason
константа, и, конечно, у меня где-то есть большая таблица, которая отображает reason
более полные объяснения (мне удалось разослать около 500 электронных писем клиентам с загадочным текстом reason
вместо одного более полного сообщения).
дальнейшее чтение
Стандарты:
Инструменты:
Интересно читает: