Возникла необходимость реализовать шину сообщений.
Предполагается следующий стандартный сценарий использования:
1. Клиент делает запрос. В ответ получает идентификатор созданной задачи. Далее делает запросы по идентификатору запроса, чтобы узнать его статус, при завершении задачи получает ответ.
2. Шина получает запрос и создаёт задачу. Если есть тот кто обработает этот тип задачи, то ему передаётся эта задача.
3. Обработчик задачи формирует ответ при успешном завершении задачи или при ошибке. Размер данных в ответе относительно небольшой, наверное до сотни килобайт (но что там будет дальше не знаю, может гораздо больше).
Здравствуйте, binks, Вы писали:
B>Возникла необходимость реализовать шину сообщений. B>Предполагается следующий стандартный сценарий использования: B>1. Клиент делает запрос. В ответ получает идентификатор созданной задачи. Далее делает запросы по идентификатору запроса, чтобы узнать его статус, при завершении задачи получает ответ. B>2. Шина получает запрос и создаёт задачу. Если есть тот кто обработает этот тип задачи, то ему передаётся эта задача. B>3. Обработчик задачи формирует ответ при успешном завершении задачи или при ошибке. Размер данных в ответе относительно небольшой,
Не очень ясно что тут сообщение. Шина это просто штука для передачи сообщений.
Клиент делает запрос: кладёт сообщение в шину в определённый топик запросов. Обработчик запроса получает сообщение, делает вычисление, создаёт сообщение-ответ и кладёт в топик ответов. Клиент получает ответ. Что за "узнать статус"?
B>наверное до сотни килобайт (но что там будет дальше не знаю, может гораздо больше).
Обычно сообщения подразумеваются мелкие, до килобайта. Если больше — делают что-то вроде аттачей: большой результат кладётся в какой-нибудь сторадж, отправляется мелкое сообщение с указанием откуда взять результат.
B>Что можно почитать по этому поводу?
Доки по какой-нибудь кафке. Гарантии доставки — at-least-once, at-most-once. Партицирование. Идемпотентность. Проблема двух генералов. correlation id. Event Sourcing.
В общем не знаю, много чего тут. Конкретные вопросы лучше задавай.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, binks, Вы писали:
B>Возникла необходимость реализовать шину сообщений. B>Предполагается следующий стандартный сценарий использования: B>1. Клиент делает запрос. В ответ получает идентификатор созданной задачи. Далее делает запросы по идентификатору запроса, чтобы узнать его статус, при завершении задачи получает ответ. B>2. Шина получает запрос и создаёт задачу. Если есть тот кто обработает этот тип задачи, то ему передаётся эта задача. B>3. Обработчик задачи формирует ответ при успешном завершении задачи или при ошибке. Размер данных в ответе относительно небольшой, наверное до сотни килобайт (но что там будет дальше не знаю, может гораздо больше).
B>Что можно почитать по этому поводу?
Я бы сначала попробовал бы написать модель этого, где все хранится в памяти, что бы посмотреть что и как, какие апи нужны шине и ее клиентам, потестировать, а потом уже пробовал бы делать "взрослую" реализацию.
Здравствуйте, ·, Вы писали:
·>Здравствуйте, binks, Вы писали:
B>>Возникла необходимость реализовать шину сообщений. B>>Предполагается следующий стандартный сценарий использования: B>>1. Клиент делает запрос. В ответ получает идентификатор созданной задачи. Далее делает запросы по идентификатору запроса, чтобы узнать его статус, при завершении задачи получает ответ. B>>2. Шина получает запрос и создаёт задачу. Если есть тот кто обработает этот тип задачи, то ему передаётся эта задача. B>>3. Обработчик задачи формирует ответ при успешном завершении задачи или при ошибке. Размер данных в ответе относительно небольшой, ·>Не очень ясно что тут сообщение. Шина это просто штука для передачи сообщений. ·>Клиент делает запрос: кладёт сообщение в шину в определённый топик запросов. Обработчик запроса получает сообщение, делает вычисление, создаёт сообщение-ответ и кладёт в топик ответов. Клиент получает ответ. Что за "узнать статус"?
Я так понимаю что, то что описал это достаточно стандартный сценарий.
Клиент кидает в "шину" HTTP запрос, получает id запроса/задачи. Задача может быть долгоиграющей. Без WebSocket, используем только polling, соответственно клиент через определённый интервал запрашивает результат по id задачи.
"Шина" это сервер, который принимает запрос, присваивает этому запросу id задачи/запроса. Раскидывает/маршрутизирует задачи на подключённые к ней сервисы.
"Шина" и сервис работают через какой-то AMPQ. Для того же RabbitMQ есть разные паттерны RPC/RequestResponse. Сам сервис не знает что есть некий id задачи. Сервис, получив запрос от "шины", выполнив работу, обратно в этот же канал отдаёт результат работы. Когда "шина" получает ответ от сервиса, то в какую-то базу пишет ответ от сервиса по id задачи.
Если клиент запрашивает результат по id задачи, то "шина" знает что ответить клиенту.
B>>наверное до сотни килобайт (но что там будет дальше не знаю, может гораздо больше). ·>Обычно сообщения подразумеваются мелкие, до килобайта. Если больше — делают что-то вроде аттачей: большой результат кладётся в какой-нибудь сторадж, отправляется мелкое сообщение с указанием откуда взять результат.
Почему именно такое ограничение?
Здравствуйте, r0nd, Вы писали:
R>Зачем? Ваша архитектура не убедила меня в использовании аж целой шины.
Очень хороший вопрос, это как раз то, что хотелось бы обсудить.
Есть сервис1, которому нужно получить информацию от сервис2.
Варианты следующие:
1. Сервис2 реализует HTTP сервер, на запросы отвечает синхронно. Пока делаю этот вариант.
2. Сервис2 реализует очередь. Входящим запросам присваивается id задачи. Результат работы можно спросить позже.
3. Шина реализует очередь. Сервис2 подключается к шине. Если нужно добавить Сервис3, то он будет пользоваться готовой инфраструктурой Шины.
Здравствуйте, Qulac, Вы писали:
Q>Я бы сначала попробовал бы написать модель этого, где все хранится в памяти, что бы посмотреть что и как, какие апи нужны шине и ее клиентам, потестировать, а потом уже пробовал бы делать "взрослую" реализацию.
Ну тут всё достаточно прозрачно.
Сервис1 говорит Сервису2 "дай информацию1". Сервис2 смотрет кэш, если данные отсутствуют, то делает дополнительную работу, если данных достаточно, то отдаёт результат.
Сам процесс уже реализован, но несколько неподходящим вариантом. Возникла потребность это исправить.
On Mar 18, 2024, 7:28 AM, binks <53352@users.rsdn.org> wrote:
B>Пытаюсь понять что мне даст реализация шины.
Даст то, что теперь без шины у вас сервисы перестанут получать уведомления. Шина, при отсутствии опыта работы с ней, приносит только горе и ошибки.
Кроме того, у вас два сервиса с примитивной логикой State Design Pattern. А pull/push notification, кстати вы забыли вариант, когда у вас подписчик на сервере создает webhook-ссылку, с помощью которой «сервер» сам при готовности задачи уведомит всех подписчиков, так вот pull/push notification можно реализовать 100500 вариантами.
⸻ ❧ “The road to success and the road to failure are almost exactly the same.” — Colin R. Davis
Здравствуйте, r0nd, Вы писали:
R>On Mar 18, 2024, 7:28 AM, binks <53352@users.rsdn.org> wrote:
R>Даст то, что теперь без шины у вас сервисы перестанут получать уведомления. Шина, при отсутствии опыта работы с ней, приносит только горе и ошибки.
Оно и понятно, что добавление ещё одного элемента в цепочку уменьшит отказоустойчивость системы.
Но хотелось бы почитать различный опыт с шиной, где подводные камни, где выиграю, где проиграю.
R>Кроме того, у вас два сервиса с примитивной логикой State Design Pattern. А pull/push notification, кстати вы забыли вариант, когда у вас подписчик на сервере создает webhook-ссылку, с помощью которой «сервер» сам при готовности задачи уведомит всех подписчиков, так вот pull/push notification можно реализовать 100500 вариантами.
Я пока рассматриваю только односторонне взаимодействие между клиентом (сервис1, кому данные нужны) и сервером (шиной). Но да, я понимаю, что это даёт дополнительную нагрузку на сервер.
Здравствуйте, binks, Вы писали:
B>Здравствуйте, r0nd, Вы писали:
R>>On Mar 18, 2024, 7:28 AM, binks <53352@users.rsdn.org> wrote:
R>>Даст то, что теперь без шины у вас сервисы перестанут получать уведомления. Шина, при отсутствии опыта работы с ней, приносит только горе и ошибки. B>Оно и понятно, что добавление ещё одного элемента в цепочку уменьшит отказоустойчивость системы. B>Но хотелось бы почитать различный опыт с шиной, где подводные камни, где выиграю, где проиграю.
R>>Кроме того, у вас два сервиса с примитивной логикой State Design Pattern. А pull/push notification, кстати вы забыли вариант, когда у вас подписчик на сервере создает webhook-ссылку, с помощью которой «сервер» сам при готовности задачи уведомит всех подписчиков, так вот pull/push notification можно реализовать 100500 вариантами. B>Я пока рассматриваю только односторонне взаимодействие между клиентом (сервис1, кому данные нужны) и сервером (шиной). Но да, я понимаю, что это даёт дополнительную нагрузку на сервер.
Тут кто-то писал, что они сделали обмен сообщениями через таблицу в бд. Вполне может быть рабочий вариант. Мне кажется, что только нужно помнить один важный момент: доставка сообщения != обработка сообщения.
Здравствуйте, Qulac, Вы писали:
Q>Тут кто-то писал, что они сделали обмен сообщениями через таблицу в бд. Вполне может быть рабочий вариант. Мне кажется, что только нужно помнить один важный момент: доставка сообщения != обработка сообщения.
В таком случае нужно чтобы оба сервиса были завязаны на эту базу данных и в случае миграции на другую базу данных придётся переписывать оба сервиса. Это не такая уже и частая нужда, но сейчас такой связности я хочу избежать. Я описал слишком абстрактно задачу, поэтому тут возникает множество вариантов.
Здравствуйте, binks, Вы писали:
B>Здравствуйте, Qulac, Вы писали:
Q>>Тут кто-то писал, что они сделали обмен сообщениями через таблицу в бд. Вполне может быть рабочий вариант. Мне кажется, что только нужно помнить один важный момент: доставка сообщения != обработка сообщения. B>Я описал слишком абстрактно задачу, поэтому тут возникает множество вариантов.
Тогда сначала нужно определить api, которое будут использовать клиенты, для доступа к шине. А под апи уже да, может быть много разных реализаций.
Здравствуйте, binks, Вы писали:
B>·>Не очень ясно что тут сообщение. Шина это просто штука для передачи сообщений. B>·>Клиент делает запрос: кладёт сообщение в шину в определённый топик запросов. Обработчик запроса получает сообщение, делает вычисление, создаёт сообщение-ответ и кладёт в топик ответов. Клиент получает ответ. Что за "узнать статус"? B>Я так понимаю что, то что описал это достаточно стандартный сценарий.
Ты описал стандартный request-response сценарий. Шина это про publish/subscribe. Потенциально несколько паблишеров и подписчиков, несколько разных топиков и т.п.
B>Клиент кидает в "шину" HTTP запрос, получает id запроса/задачи. Задача может быть долгоиграющей. Без WebSocket, используем только polling, соответственно клиент через определённый интервал запрашивает результат по id задачи.
В случае шины poll у тебя должен быть только способ транспорта сообщений (при отсуствии возможностей сделать нормальный websocket/etc). По poll нужно просто тупо "ждать" сообщений, любых, без привязки к какому-либо id.
Скажем, в случае UI типично будет как-то так:
1. Юзер жмёт кнопку.
2. Создаётся uuid correlation id и сообщение "команда", описывающее действие юзера.
3. В UI меняется состояние, отображаются "песочные часы", ожидание результата действия для данного id. Или просто "спасибо, понял".
4. Когда приходит сообщение в UI сопоставляется как дальше поменять состояние ui в зависимости от содержимого сообщения.
B>"Шина" это сервер, который принимает запрос, присваивает этому запросу id задачи/запроса. Раскидывает/маршрутизирует задачи на подключённые к ней сервисы.
Нет.
B>"Шина" и сервис работают через какой-то AMPQ. Для того же RabbitMQ есть разные паттерны RPC/RequestResponse. Сам сервис не знает что есть некий id задачи. Сервис, получив запрос от "шины", выполнив работу, обратно в этот же канал отдаёт результат работы. Когда "шина" получает ответ от сервиса, то в какую-то базу пишет ответ от сервиса по id задачи. B>Если клиент запрашивает результат по id задачи, то "шина" знает что ответить клиенту.
Это скорее всего некий gateway, которые ковертирует request/response пару в поток сообщений туда-сюда.
Суть в том, что с шиной у тебя нет явно связи между сервисами/серверами, кроме как через AMPQ/аналог.
Например, вначале ты реализовал так, что твой request преобразовался в сообщение, потом это сообщение обработалось кем-то и ответ положился обратно, сформировав response:
Или на одно и то же сообщение иметь несколько подписчиков. Т.е. юзер шлёт какую-то команду у себя в браузере и видит результат выполнения этой команды и в во всех открытых закладках браузера, и на мобилке и т.п.
Инымы словами, шина нужна не для того, чтобы похитрее типичный Request/Response реализовать, шоб враг не догадался как это всё работает, а для более гибкой передачи данных, чем Request/Response.
B>>>наверное до сотни килобайт (но что там будет дальше не знаю, может гораздо больше). B>·>Обычно сообщения подразумеваются мелкие, до килобайта. Если больше — делают что-то вроде аттачей: большой результат кладётся в какой-нибудь сторадж, отправляется мелкое сообщение с указанием откуда взять результат. B>Почему именно такое ограничение?
Потому что размер сообщения обычно определяет отзывчивость системы. Сообщения передаются и обрабатываются последовательно. Пока у тебя передаётся гиговое сообщение, другие сообщения ждут своей очереди. Ещё часто сообщения могут быть получены несколькими подписчиками, но не всем им необходим полный результат. Ещё для сообщений бывают redelivery при обрыве связи.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, r0nd, Вы писали:
B>>Возникла необходимость реализовать шину сообщений. R>Зачем? Ваша архитектура не убедила меня в использовании аж целой шины.
У нас была похожая арх-ра с шиной (rabbitmq) -- были запросы от пользователей с большими файлами на обработку, файлы
хранились на шаре, в шину отправлялся запрос на обработку и далее по шине клиент получал статус обработки запроса.
С одной стороны шина тут не очень чтобы очень, особенно учитывая не самый высокий уровень нагрузки, но с др. стороны
есть плюшки типа потери данных, т.к. шина может хранить сообщения или если обработчик свалился во время обработки, то
его можно будет перезапустить энное число раз, да и обработчиков этих можно много наподключать, так что решение до какой-то
степени будет масштабируемым.
S>С одной стороны шина тут не очень чтобы очень, особенно учитывая не самый высокий уровень нагрузки, но с др. стороны S>есть плюшки типа потери данных
Классика: разделение системы на части:
* "тупое хранилище" — простая и надежная часть
* "бизнес-логика" — нагромождение спагетти, падучее и нервное
Когда не уверен в том, что можешь сделать надежный сервис, приходится подстилать соломку в виде отдельного "хранилища запросов", будь то MQ или что-то еще в этом роде. Сложная система обычно уступает простой по надежности. Математика: 100 связанных компонентов, каждый из которых имеет 99% надежность, имеют общую надежность ~37%. И психология: наш мозг не умеет удалять, поэтому решает проблему слишком сложной системы дальнейшим увеличением сложности (добавлением еще одного компонента, message queue/bus). Метрика, за которой гонятся, как правило не включает требования к latency. Оттого и так популярны все эти "retry mechanisms".
Спасибо. Очень доступно объяснил. В плане терминологии стало более понятно.
В итоге шина может решать конвейерную обработку сообщений, либо как удобная инфраструктура общения между участниками шины.
Для моей задачи нет необходимости делать шину. Мне нужно(если вообще нужно) использовать request-response/rpc.
Структура с правильной терминологией:
[HTTP Client] -- [HTTP Server (AMPQ Gateway)] -- [Service A (AMPQ Node)]
Чтобы иметь id задачи необходимо хранить эти задачи и доставать для них результаты, RabbitMQ для этой роли не очень хорошо подходит и нужно использовать какое-то своё хранилище.
Я правильно понимаю, что нет такого инструмента два-в-одном?
Здравствуйте, binks, Вы писали:
B>Чтобы иметь id задачи необходимо хранить эти задачи и доставать для них результаты, RabbitMQ для этой роли не очень хорошо подходит и нужно использовать какое-то своё хранилище.
А в чём цель использовать сообщения? Какая задача у MQ? Где есть какое состояние?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>А в чём цель использовать сообщения? Какая задача у MQ? Где есть какое состояние?
Не текущая задач, но, например, если в качестве сервиса будет корпоративная почтовая рассылка.
Здравствуйте, binks, Вы писали:
B>·>А в чём цель использовать сообщения? Какая задача у MQ? Где есть какое состояние? B>Не текущая задач,
Т.е. ищешь серебрянную пулю?
B>но, например, если в качестве сервиса будет корпоративная почтовая рассылка.
Что тогда будет ответом от такого сервиса? Который http клиент будет поллить в ожидании?
В этом конкретном случае тебе просто понадобится транзакционный сервис почтовых сообщений, а не искать "инструмент два-в-одном".
Впрочем, ты сказал "рассылка", тут вообще fire and forget. Не отправилось, так не отправилось, ну и реакции на невалидные емейлы, отписки, отправку в спам, етс. Т.е. это очень специализировання задача с соответствующим инструментарием.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, binks, Вы писали:
B>Сервис1 говорит Сервису2 "дай информацию1". Сервис2 смотрет кэш, если данные отсутствуют, то делает дополнительную работу, если данных достаточно, то отдаёт результат.
Не похоже что тут вообще нужна шина. B>Сам процесс уже реализован, но несколько неподходящим вариантом. Возникла потребность это исправить.
А каким вариантом?
Здравствуйте, binks, Вы писали:
B>Чтобы иметь id задачи необходимо хранить эти задачи и доставать для них результаты, RabbitMQ для этой роли не очень хорошо подходит и нужно использовать какое-то своё хранилище. B>Я правильно понимаю, что нет такого инструмента два-в-одном?
В целом для реализации нужна:
— БД (для хранения статусов задач)
— брокер сообщений (для хранения и обработки задач)
— http app для выдачи состояния задачи наружу
Решения два-в-одном есть, например, celery (python, БД + брокер) + flower к нему (http api).
Здравствуйте, ·, Вы писали:
B>>Не текущая задач, ·>Т.е. ищешь серебрянную пулю?
Хочу определить рамки архитектурного решения.
B>>но, например, если в качестве сервиса будет корпоративная почтовая рассылка. ·>Что тогда будет ответом от такого сервиса? Который http клиент будет поллить в ожидании?
Статус отправки сообщения.
·>В этом конкретном случае тебе просто понадобится транзакционный сервис почтовых сообщений, а не искать "инструмент два-в-одном". ·>Впрочем, ты сказал "рассылка", тут вообще fire and forget. Не отправилось, так не отправилось, ну и реакции на невалидные емейлы, отписки, отправку в спам, етс. Т.е. это очень специализировання задача с соответствующим инструментарием.
В данном случае это рассылка, которая требует понимания того, отправилось ли сообщение
Здравствуйте, Kernan, Вы писали:
K>Здравствуйте, binks, Вы писали:
B>>Сервис1 говорит Сервису2 "дай информацию1". Сервис2 смотрет кэш, если данные отсутствуют, то делает дополнительную работу, если данных достаточно, то отдаёт результат. K>Не похоже что тут вообще нужна шина.
В процессе обсуждения в этой теме именно такое понимание и пришло.
B>>Сам процесс уже реализован, но несколько неподходящим вариантом. Возникла потребность это исправить. K>А каким вариантом?
К сути обсуждения это не имеет прямого отношения. Мне нужно вынести логику обработки из базы в отдельное приложение и я хотел понять уместно ли здесь использовать шину.
Здравствуйте, Буравчик, Вы писали:
Б>Решения два-в-одном есть, например, celery (python, БД + брокер) + flower к нему (http api).
Насколько я помню, celery тоже не решает задачи два-в-одном. Celery предоставляет обёртку для удобного использования из питона. Да и код у меня не на питоне.
Здравствуйте, binks, Вы писали:
B>Здравствуйте, Буравчик, Вы писали:
Б>>Решения два-в-одном есть, например, celery (python, БД + брокер) + flower к нему (http api). B>Насколько я помню, celery тоже не решает задачи два-в-одном. Celery предоставляет обёртку для удобного использования из питона. Да и код у меня не на питоне.
Ну, я скорее подразумевал "готовое решение". А с питоном ты прогадал, да
Здравствуйте, binks, Вы писали:
B>>>Не текущая задач, B>·>Т.е. ищешь серебрянную пулю? B>Хочу определить рамки архитектурного решения.
Задача неясна.
B>·>Что тогда будет ответом от такого сервиса? Который http клиент будет поллить в ожидании? B>Статус отправки сообщения.
Тоже всё очень условно..
B>·>В этом конкретном случае тебе просто понадобится транзакционный сервис почтовых сообщений, а не искать "инструмент два-в-одном". B>·>Впрочем, ты сказал "рассылка", тут вообще fire and forget. Не отправилось, так не отправилось, ну и реакции на невалидные емейлы, отписки, отправку в спам, етс. Т.е. это очень специализировання задача с соответствующим инструментарием. B>В данном случае это рассылка, которая требует понимания того, отправилось ли сообщение
Это же smtp. Под "Отправилось ли" можно понимать разные вещи. Если у тебя есть smtp сервер рядом, то это может означать, что сервер ответил "OK" о том, что он получил твоё мыло, что обычно занимает несколько миллисекунд, и вообще никакие очереди не нужны. А может означать, что он удачно отправил на сервер получателя, а это афаир может занимать до недели. И тут http-полл становится, мягко говоря, бессмысленным.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай