Речь не «сообщениях» или «событиях», речь о «джобах» - задача имеющая состояние “pending”, “in-progress”, “success” or “failed” + надежность выполнения важнее скорости + нужна история джоб
И именно для таких «джоб» я предпочитаю использовать БД и вот что БД дают, чего не дают очереди:
. Абсолютный контроль - достаем столько задач сколько хотим и как хотим, полное управление загрузкой и приоритетами . Последовательная куча - если вам нужно последовательно обрабатывать данные (FIFO) по сущности вам придется делать очередь на каждый экземпляр сущности, в случае работу через БД вы сваливаете все в одну кучу, а воркеры сами поддерживают последовательность просто доставая их поочереди . Чтобы перезапустить просто меняем статус в “pending” . История джоб . Чтобы узнать что либо о состоянии джоб, просто делаем запрос в таблицы / коллекции . Легко считать метрики . Реальная only once гарантия (такое вообще никто другой не даст)
Из недостатков:
. Вам придется или создавать какие-то триггеры появления работы или ходить время от времени в БД самому . Обычно это немалая нагрузка на БД . Подходит, только если вам важнее надежность выполнения, а не скорость
Пока что я делал самостоятельно такие очереди на БД, но конечно лучше попробовать что-нибудь типа PgQ или подобные плагины
A
Arsen ИТ-К Arakelyan
2023-09-15 16:36
Интересная мысль, а почему тогда эти софтины такие популярные?
Может все-таки есть какие-то особенности которые делают их более предпочтительными чем базы данных?
🦾
🦾 IT-Качалка Давида Шекунца 💪
2023-09-15 17:02
Очереди выгодны:
– Если тебе нужно "отправил и как дойдет и обработаю можно забыть" – Если тебе нужно "отправил и дошло как только возможно" – Если тебе достаточно гарантии at least once – Если тебе нужна большая пропускная способность – Если нужен бродкаст ("отправил и дошло до многих") – Если тебе нужны Event ("отправил и достаточно надежно дошло до многих") – Если тебе нужен Request / Response но несинхронный (типа HTTP, gRPC)
А с джобами чаще всего происходит так: люди сохраняют их в БД, потом пихают в очередь id, потом достают из очереди id и из БД джобу и делают ее. А если надо ретраить, то они еще раз проворачивают весь цикл.
Почему? "Потому что у нас есть очередь, а нам надо обрабатывать поочереди, там есть корень 'очередь', значит будем использовать очередь"
Но в данной ситуации очередь не добавляет пользы и наоборот только усложняет систему.
🦾
🦾 IT-Качалка Давида Шекунца 💪
2023-09-15 17:03
А еще есть Message Broker – если очередь это TCP, то Message Broker это UDP
То есть Message Broker чаще всего имеет низкие гарантии доставки и не хранит ничего на дисках, НО именно поэтому они намного быстрее, легковеснее и распределеннее
И тут тоже многие люди делают, например, RPC или Event через MQ, в тот момент, как им гораздо лучше мог подойти именно Message Broker
Почему? Потому что люди привыкли: "Чтобы отправить сообщение HTTP и есть очереди" – и дальше этого черного куба не смотрят
Самый крутой Message Broker, который я использовал это Nats.io
🦾
🦾 IT-Качалка Давида Шекунца 💪
2023-09-15 17:08
А еще есть золотая середина между MQ и MB – это протокол MQTT
Протокол, который используется в IoT, поэтому мало о нем кто знает, но на деле Google Pub/Sub под собой именно его и реализует
Это MB но с большой гарантией доставки и скоростью
Самый классный MQTT броке VerneMQ
A
Arthur G
2023-09-15 18:26
Тоже так делал. Хороший вариант.
Но вот only once тут только переключение состояния, с остальным тоже нужно что-то делать.
Вроде это не сложно, если знаешь, так что не минус, а особенность. 😁
I
Ivan Zhuravlev
2023-09-25 06:56
Из минусов: на Kafka нельзя построить регулярные очереди простым способом. На rabbitmq планировщик работает в рамках одного инстанса, не умеет в распределённость.
Из плюсов: На БД регулярные очереди элементарная задача. Как и написание любой кастомной логики поверх очереди.
I
Ivan Zhuravlev
2023-09-25 07:01
У dapr про акторы хорошо написано и на сайте msdn есть раздел
🦾
🦾 IT-Качалка Давида Шекунца 💪
2023-09-25 07:22
Оооо я ждал тебя 😍
У нас было 2 задачи, в которых я разочаровался при использовании очередей:
- Джобы, которые запускаются раз в месяц и должны (1) посчитать сколько использовали компании ресурсов, (2) рассчитать сколько снять денег с баланса с учетом скидок и подобного, (3) снять деньги с баланса системы а за остатком сходить в сторонюю систему, (4) выставить счета и акты, (4) подписать и отправить на почту
- Есть контроллеры (порядка 10-50к) каждый отправляет сообщения, которые обязательно нужно процессить посследовательно в рамках 1 контроллера, но параллельно в рамках множества (потому что присылают они с разной частатой и процессинг некоторых может занимать секунды из-за ожидания сторонних провайдеров)
Сразу скажу: в обоих случаях скорость на четвертом месте, самое важное (1) надежность исполнения, (2) удобство добавления новых фич (этот функционал активно расширяется), (3) дебагинг и возможность быстрого перезапуска в случае проблем (а проблем с ними обычно куча потому что мы завязаны на кучу сторонних сисем)
В первом случае БД дает преимущества, потому что (1) мы вывели все джобы с состояниями тех поддержке и они могут посмотреть что и как по каждой джобе, а также что и как было выставлено, (2) также прямо в UI могут выбрать "с какого этапа повторить", на джобе храняться все id созданных сущностей и мы удаляем все созданныеы сущности до нужного этапа и продолжаем взятие в работу, (3) преимущество из пункта ниже
Во втором случае самая большая проблема "последовательно в рамках 1 контроллера и параллельно во всех", где контроллеров >10 000, партиции слишком жирно создавать на каждый отдельный контроллер, а если создавать топики, то надо будет вертеть систему привязки N топиков к M сервисов (и вот эта вся распределенщина)
А мы все масштабирование решили в 1 строчку: UPDATE job SET status='in-progress' WHERE id = (SELECT id FROM job WHERE status = 'pending' AND controller_id NOT IN ( SELECT controller_id FROM job WHERE status = 'in-progress') ORDER BY created_at ASC LIMIT 1) RETURNING * – теперь каждый отдельный инстанс из одной кучи конкурентно выхватывает какую-то задачу учитывая последовательность в рамках 1 и параллельность в рамках множества
Да, несомненно, скоростью тут и не пахнет (хотя и проблем со скоростью у нас нет)
Ну и соответственно все остальные плюсы из текста поста
Я это к чему: да, несомненно, все это можно сделать более крутым в каждом аспекте на очередях, НО я достигаю всех нужных показателей для средних проектов, еще и удобной разработки, еще и распределенности просто парой INSERT, SELECT, UPDATE, используя уже используемую в проекте БД, от которой мне нужно только наличие атомарного UPDATE и все, и это очень радует
P.S.
Статью с огромным удовольствием почитаю)
I
Ivan Zhuravlev
2023-09-25 07:32
🤗 Да, в твоём кейсе как раз одной кафкой просто невозможно это решить, нужно строить материализованные представления для работы с данными и там со всеми решениями вокруг этого стэка. А по регулярным задачам как раз вот в статье целое решение с кучей аспектов, реализовать только регулярные задачи с надёжностью не тривиальная задача оказалась. Я себе напоминание поставил, в ЛС скину ссылку, пока в коммандировке с инетом проблема, телега еле грузит)
БД намного дешевле и быстрее в реализации этой всей логике, я об этом и написал в плюсах)