Як керувати автоматизованими електронними листами, надісланими з веб-програми


12

Я розробляю веб-додаток, і мені цікаво, як створити архітектуру для управління відправленням автоматизованих електронних листів.

В даний час у мене є вбудована функція в моє веб-додаток, і електронні листи надсилаються на основі введення / взаємодії користувачів (наприклад, створення нового користувача). Проблема полягає в тому, що підключення безпосередньо до поштового сервера займає пару секунд. Масштабувавши мою заявку вгору, це буде значним вирізом пляшки в майбутньому.

Який найкращий спосіб керувати надсиланням великої кількості автоматизованих електронних листів у моїй архітектурі системи?

Не надсилатиметься величезна кількість електронних листів (максимум 2000 в день). Електронні листи не потрібно надсилати негайно, затримка до 10 хвилин - це нормально.

Оновлення: Черга повідомлень була надана як відповідь, але як це було б розроблено? Чи буде це оброблятися в додатку та оброблятися протягом тихого періоду, чи мені потрібно створити новий "поштовий додаток" чи веб-сервіс, щоб просто керувати чергою?


Чи можете ви дати нам грубе відчуття масштабу? Сотні, тисячі чи мільйони листів? Крім того, чи потрібно негайно надсилати електронні листи чи допустимий невеликий відставання?
янніс

Надсилання електронної пошти передбачає передачу SMTP-повідомлення хосту, який отримує пошту, але це не означає, що повідомлення було фактично доставлено. Таким чином, все надсилання електронної пошти є асинхронним, і немає сенсу робити вигляд, що "чекати успіху".
Кіліан Фот

1
Я не "чекаю успіху", але мені доведеться чекати, коли сервер smtp прийме мій запит. @YannisRizos дивіться оновлення RE ваш коментар
Gaz_Edge

Для 2000 (що є вашим описаним максимумом) листів він просто працюватиме. Коли вони трапляються за 10 робочих годин, це 3 повідомлення в хвилину, що дуже можливо. Просто переконайтесь, що ви настроїли свою DNS-запис добре, і провайдер приймає їх надсилати в цих кількостях. Також подумайте над тим: "що поштовий сервер не працює?". Навантаження для надсилання 2000 листів - не про що турбуватися.
Люк Франкен

Відповідь, де знаходиться CRONTAB
Тулайн Кордова

Відповіді:


15

Загальний підхід, як вже говорилося в Ozz , - це черга повідомлень . З точки зору дизайну, черга повідомлень по суті є чергою FIFO , яка є досить фундаментальним типом даних:

Черга 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а не повним повідомленням).

Подальше читання

Стандарти:

Інструменти:

Цікаво читається:


Ого. Просто про найкращу відповідь, яку я коли-небудь отримував тут! Не можу вам подякувати достатньо!
Gaz_Edge

Я, і впевнений, мільйони інших людей використовують цей FIFO з Gmail і Google Apps Script. Фільтр Gmail мітить будь-яку вхідну пошту на основі критеріїв, і це все, їх чергами. Сценарій Google Apps запускається кожні тривалість X, отримує перші y повідомлення, надсилає їх, видаляє їх. Промийте та повторіть.
DavChana

6

Вам потрібна якась система черги.

Одним із простих способів може бути запис у таблицю бази даних та наявність інших зовнішніх рядків процесу додатків у цій таблиці, але існує багато інших технологій створення черги.

Ви можете мати важливе значення для електронних листів, так що деякі з них будуть діяти майже негайно (наприклад, скидання пароля), а ті, що мають меншу важливість, можуть бути зібрані для надсилання пізніше.


чи є у вас діаграма архітектури чи приклад, який показує, як це працює? Наприклад, чи стоїть черга в іншому "додатку", наприклад, поштовому додатку, чи процес отримується з веб-програми протягом спокійного періоду. Або я повинен створити якусь веб-службу, щоб обробити їх?
Gaz_Edge

1
@Gaz_Edge Додаток додає елементи до черги. Фоновий процес (найімовірніше, сценарій крон) вискакує x елементи з черги кожні n секунд та обробляє їх (у вашому випадку надсилає електронний лист). Таблиця однієї бази даних прекрасно працює як зберігання в черзі для невеликої кількості елементів, але в цілому операції запису в базі даних є дорогими, а для більшої кількості ви можете поглянути на такі сервіси, як SQS Amazon .
янніс

1
@Gaz_Edge Я не впевнений, що зможу це зробити схематично простіше, ніж те, що я написав: "... запишіть у таблицю бази даних і в цій таблиці є інші рядки зовнішнього процесу застосування ....", а для таблиці прочитайте "будь-яку чергу "яка б технологія не була.
ozz

1
(продовження ...) Ви можете побудувати фоновий процес, який очищає чергу, таким чином, що враховує ваш трафік, наприклад, ви можете доручити йому обробляти менше елементів (або взагалі жодних) у періоди, коли ваш сервер знаходиться під напругою. . Вам доведеться або передбачити ці напружені часи, переглянувши свої минулі дані про трафік (простіше, ніж це звучить, але з великою помилкою) або за допомогою фонового процесу перевіряти стан трафіку щоразу, коли він працює (точніше, але додані накладні витрати рідко потрібні).
янніс

@YannisRizos хочете поєднати ваші коментарі з відповіддю? Також діаграми архітектури та конструкції будуть корисні (я вирішив цього разу отримати їх з цього питання !;-))
Gaz_Edge

2

Не надсилатиметься величезна кількість електронних листів (максимум 2000 в день).

На додаток до черги, другим, що вам слід враховувати, є надсилання електронних листів через спеціалізовані сервіси: наприклад, MailChimp (я не пов'язаний з цією послугою). Інакше багато поштових служб, таких як gmail, незабаром відправлять ваші листи в папку зі спамом.


2

Я моделював свою систему черг в різних 2-х таблицях як;

CREATE TABLE [dbo].[wMessages](
  [Id] [uniqueidentifier]  NOT NULL,
  [FromAddress] [nvarchar](255) NOT NULL,
  [FromDisplayName] [nvarchar](255) NULL,
  [ToAddress] [nvarchar](255) NOT NULL,
  [ToDisplayName] [nvarchar](255) NULL,
  [Graph] [xml] NOT NULL,
  [Priority] [int] NOT NULL,
  PRIMARY KEY CLUSTERED ( [Id] ASC ))

CREATE TABLE [dbo].[wMessageStates](
  [MessageId] [uniqueidentifier] NOT NULL,
  [Status] [int] NOT NULL,
  [LastChange] [datetimeoffset](7) NOT NULL,
  [SendAfter] [datetimeoffset](7) NULL,
  [SendBefore] [datetimeoffset](7) NULL,
  [DeleteAfter] [datetimeoffset](7) NULL,
  [SendDate] [datetimeoffset](7) NULL,
  PRIMARY KEY CLUSTERED ( [MessageId] ASC )) ON [PRIMARY]
) ON [PRIMARY]

Між цими таблицями існує відношення 1-1.

Таблиця повідомлень для зберігання вмісту повідомлення. Фактичний вміст (To, CC, BCC, Subject, Body тощо) серіалізується у поле Graph у форматі XML. Інше З, До інформації просто використовується для повідомлення про проблеми без деріаріалізації графіка. Виділення цієї таблиці дозволяє розділити вміст таблиці розділів на інший дисковий накопичувач. Після того, як ви готові надіслати повідомлення, вам потрібно прочитати всю інформацію, тому нічого поганого в тому, щоб серіалізувати весь вміст до одного стовпця з індексом первинного ключа.

Таблиця MessageState для зберігання стану вмісту повідомлення з додатковою інформацією на основі дати. Відокремлення цієї таблиці дозволяє отримати механізм швидкого доступу з додатковими індексами на швидкому IO-сховищі. Інші стовпці вже пояснюють себе.

Ви можете використовувати окремий пул потоків, який сканує ці таблиці. Якщо програма та пул живуть на одній машині, ви можете використовувати клас EventWaitHandle для сигналізації пулу від програми про щось вставлене в ці таблиці, інакше періодичне сканування з тайм-аутом є найкращим.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.