Загальний підхід, як вже говорилося в Ozz , - це черга повідомлень . З точки зору дизайну, черга повідомлень по суті є чергою FIFO , яка є досить фундаментальним типом даних:
Особливість черги повідомлень - це те, що, хоча ваша програма несе відповідальність за чергування, інший процес несе відповідальність за виведення з черги. У лінгво-черзі ваша програма є відправником повідомлення (-ів), а процес де-черги - одержувачем. Очевидною перевагою є те, що весь процес є асинхронним, одержувач працює незалежно від відправника, доки є повідомлення для обробки. Очевидним недоліком є те, що вам потрібен додатковий компонент, відправник, щоб вся справа працювала.
Оскільки ваша архітектура тепер покладається на два компоненти для обміну повідомленнями, ви можете використовувати фантазійний термін міжпроцесового спілкування для цього.
Як введення черги впливає на дизайн вашої програми?
Певні дії у вашій програмі генерують електронні листи. Введення черги повідомлень означало б, що ці дії тепер повинні підштовхувати повідомлення до черги (і більше нічого). Ці повідомлення повинні містити мінімальну кількість інформації, необхідної для створення електронних листів, коли ваш отримувач отримує їх для обробки.
Формат та зміст повідомлень
Формат і зміст ваших повідомлень повністю залежить від вас, але вам слід пам’ятати, чим менше, тим краще. Ваша черга повинна бути якнайшвидшою для запису та обробки, наскільки це можливо, викидання в неї основної маси даних, ймовірно, створить вузьке місце.
Крім того, декілька хмарних сервісів черги мають обмеження на розміри повідомлень і можуть розділяти великі повідомлення. Ви не помітите, що розділені повідомлення будуть подані як одне, коли ви запитаєте їх, але з вас буде стягнуто кілька повідомлень (якщо, звичайно, ви користуєтесь послугою, яка вимагає плати).
Конструкція приймача
Оскільки ми говоримо про веб-додаток, загальним підходом для вашого приймача буде простий скрипт cron. Він буде працювати щохвилини x
(або секунди), і це:
- Поп-
n
кількість повідомлень із черги,
- Обробляйте повідомлення (тобто надсилайте електронні листи).
Зауважте, що я кажу поп замість того, щоб отримати чи отримати, це тому, що ваш приймач не просто отримує елементи з черги, але й очищає їх (тобто видаляє їх з черги або позначає їх як оброблені). Як саме це станеться, залежить від вашої реалізації черги повідомлень та конкретних потреб вашої програми.
Звичайно, що я описую, це по суті пакетна операція , найпростіший спосіб обробки черги. Залежно від ваших потреб, ви, можливо, захочете обробляти повідомлення складніше (це також вимагатиме складнішої черги).
Трафік
Ваш приймач може враховувати трафік і коригувати кількість повідомлень, які він обробляє, залежно від трафіку на час його запуску. Спрощеним підходом було б спрогнозувати ваші великі години руху на основі даних про минулий трафік та припускаючи, що ви їздили зі сценарієм 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
а не повним повідомленням).
Подальше читання
Стандарти:
Інструменти:
Цікаво читається: