Представьте, что вы запускаете интернет-магазин. Покупательница Leyla оформляет заказ на 87 AZN, и сразу после этого должно произойти сразу несколько вещей: заказ сохраняется в базе, склад резервирует товар, а Leyla получает письмо с подтверждением. Вопрос в том, как сервисы сообщают друг другу о случившемся. От ответа зависит, переживёт ли магазин «чёрную пятницу» и падение одного-единственного сервиса. В этом уроке разберём два способа общения — синхронный и асинхронный, познакомимся с брокерами сообщений и выясним, какие гарантии доставки они дают.
Синхронное общение: цепочка HTTP-вызовов
Самый очевидный способ — синхронный: один сервис вызывает другой по HTTP и ждёт ответа, как при телефонном звонке. Пока собеседник не ответил, положить трубку нельзя. Вот как выглядит обработчик заказа в сервисе orders (Express 5), который по очереди вызывает склад и сервис писем:
app.post('/orders', async (req, res) => {
const order = await saveOrder(req.body);
await fetch('http://inventory/reserve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orderId: order.id }),
});
await fetch('http://notifications/send-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orderId: order.id }),
});
res.status(201).json(order);
});Код короткий и понятный, но в нём спрятаны три проблемы.
Три проблемы цепочки вызовов
- Отказы каскадом. Заказ завершится, только если все сервисы ответили. Доступность перемножается: три сервиса по 99,9 % (около 9 часов простоя в год каждый) вместе дают 0,999³ ≈ 99,7 % — уже около 26 часов. Упал сервис писем — перестали приниматься и заказы, хотя письмо не главное.
- Задержки складываются. Клиент ждёт сумму всех вызовов: три вызова по 200 мс — это 600 мс, а если один сервис «задумался» на 5 секунд, столько же ждёт и покупатель.
- Жёсткая связность. Сервис заказов знает адреса
inventoryиnotificationsи формат каждого запроса. Хотите добавить аналитику — придётся менять и заново выкатывать сервис заказов.
Есть и четвёртая, самая неприятная: частичный успех. Склад уже зарезервировал товар, а вызов сервиса писем упал. Покупательница видит ошибку 500, жмёт «Оплатить» ещё раз и создаёт дубль, а резерв приходится откатывать вручную.
⚠️ Автоматические повторы (
retry) в такой цепочке только усугубляют ситуацию: и без того перегруженный сервис получает ещё больше запросов.
Асинхронное общение: сообщения и брокер
Выход — не звонить, а оставить сообщение. Представьте кухню ресторана: официант не стоит над поваром, а прикалывает бланк заказа на стойку и идёт к следующему столу. Повара забирают бланки, когда освобождаются. Если повар ушёл на перерыв, бланки ждут на стойке и не теряются.
В программировании то же самое описывают пятью терминами: