Пошук уроків, статей та іншого контенту
Як розв'язати компоненти системи в часі — виробники й споживачі, що не чекають одне одного.
Реєстрація нового користувача може потребувати кількох дій: зберегти запис у базі даних, надіслати вітальний email, згенерувати мініатюру аватара, повідомити систему аналітики. Виконання геть усього цього синхронно, у межах одного HTTP-запиту, означає, що користувач чекає відповіді сервера, доки завершиться найповільніша з цих дій (типово — надсилання email через зовнішній сервіс), навіть якщо сама реєстрація вже логічно завершена.
Замість виконання всього синхронно, застосунок (виробник, producer) кладе повідомлення в чергу («потрібно надіслати вітальний email користувачу X») і одразу повертає відповідь користувачу — не чекаючи фактичного виконання. Окремий процес (споживач, consumer) забирає повідомлення з черги й виконує реальну роботу асинхронно, у свій час, незалежно від початкового запиту.
Запит реєстрації → Застосунок → зберегти в БД → повернути відповідь користувачу ОДРАЗУ
│
└──→ покласти повідомлення в чергу
│
↓ (асинхронно, окремо)
Consumer: надіслати emailКористувач отримує швидку відповідь, не чекаючи повільних, не критичних для самого запиту дій.
Тимчасова недоступність споживача (наприклад, сервіс надсилання email лежить) не блокує основну операцію (реєстрацію) — повідомлення просто чекає в черзі, доки споживач не відновиться.
Виробник і споживач можна масштабувати незалежно один від одного — більше споживачів для обробки черги, що росте, без зміни коду виробника.
Надсилання email/push-сповіщень — не критично для відповіді користувачу тут і зараз.
Обробка завантажених файлів (генерація мініатюр зображень, транскодування відео) — потенційно повільна операція, яку немає сенсу змушувати користувача чекати синхронно.
Логування подій аналітики — застосунок продовжує роботу, не блокуючись на записі кожної окремої події десь у сховищі аналітики.
Це той самий принцип, що й неблокувальна модель Node.js (курс Node.js, урок про Event Loop) — не змушувати одну повільну операцію блокувати геть усе інше, лише застосований на рівні архітектури цілої розподіленої системи, а не одного процесу.
Черги повідомлень додають реальну складність — потрібна сама інфраструктура черги, обробка ситуації, коли споживач падає посеред обробки повідомлення (чи потрібно повторити спробу, і скільки разів), моніторинг довжини черги. Це виправдано для дій, які дійсно можуть почекати, — але не для дій, результат яких користувач має побачити негайно (сама операція реєстрації, підтвердження оплати).
Класти в чергу дію, результат якої користувач очікує побачити негайно в тій самій відповіді, — асинхронна обробка тут лише додає плутанини й затримки там, де синхронна відповідь була доречною.
Не обробляти сценарій падіння споживача посеред обробки повідомлення — повідомлення може загубитись назавжди без явної стратегії повторних спроб чи підтвердження обробки.
Ігнорувати зростання довжини черги без моніторингу — якщо споживачі не встигають за темпом виробників, черга росте необмежено, а затримка обробки для кожного нового повідомлення стає дедалі гіршою непомітно.
Черга повідомлень розв'язує виробника (застосунок, що ініціює дію) і споживача (процес, що реально виконує роботу) у часі — виробник кладе повідомлення й одразу продовжує далі, споживач забирає й обробляє асинхронно, незалежно. Це прискорює відповідь користувачу й ізолює тимчасову недоступність повільних залежностей, ціною додаткової інфраструктурної складності — виправданої для дій, які дійсно можуть почекати, а не для критичного шляху самого запиту.