Pull to refresh

Comments 7

Спасибо за статью, очень любопытный опыт.
Какими ты ресурсами пользовался при дизайне системы? Имею в виду на что опирался при построении архитектуры ?

  1. Какие были требования?

  2. Какие лучшие практики использовал?

  3. Чем вдохновлялся?

Я например ориентируюсь на популярные паттерны в других решениях (дабы не городить свой велосипед) (вот напр интересная статья) и архитектурные рекомендации AWS architecture center.

Пример:


P.S.: ИМХО в подобных статьях было бы здорово добавлять такую картинку, это бы сильно упростило понимание архитектуры.

  1. Требования я сам себе ставил: простота, надежность, расширяемость, масштабируемость.

  2. Из лучших практик - это разделение бота на отдельные, параллельно работающие сервисы, взаимодействующие между собой через обработку очередей сообщений.

  3. Вдохновлялся я пожалуй - устройством и работой пулемета Maxim :)

Я полагал, что описываемая архитектура бота настолько проста, что достаточно текста, да и художник из меня - не очень, но раз надо графику, то вот что у меня получилось:

Почему не воспользовались обычными брокерами сообщений вроде RabbitMQ/Kafka? Как по мне, использование БД в качестве очередей - как изобретение велосипеда наполовину

Потому, что данном случае это избыточно. Зачем простую систему усложнять дополнительными сущностями и слоями взаимодействия?

Брокеры сообщений уместны: а) в распределенных системах, б) для обслуживания очередей обмена сообщениями между несколькими компонентами системы.

Тут не нужен сетевой уровень обмена, все процессы находятся в памяти одного севера и каждая очередь используется даже не для обмена, а для простой передачи сообщений от одного процесса к другому, т.е. между всего двумя процессами.

И придумывать тут особо ничего не надо, по сути любая таблица в БД с автоинкрементным ID или полем даты добавления записи - это уже очередь, в конец которой один процесс может добавлять, а другой - вынимать сообщения из начала.

Потому, что данном случае это избыточно. Зачем простую систему усложнять дополнительными сущностями и слоями взаимодействия?

Тогда уж и бд избыточна! Можно же просто в файлик писать. Раз сервис простой, два процесса...

один процесс может добавлять, а другой - вынимать сообщения из начала

Это описание Pull-модели, и она не очень подходит для real-time взаимодействия. Вам надо будет опрашивать бд однотипными запросами. А это чтение с диска. И чем больше вы захотите приблизиться к real-time, тем больше придется дидосить бд. Тогда уж проще взять Redis, у него кстате тоже есть очереди. И это я ещё молчу о том, что ещё надо как-то отправлять запрос на удаление записей. И тут либо вы делаете лишний запрос каждый раз, либо придется писать кастомную систему удаления, основанной на какой-то эвристической модели или по крону, что действительно является избыточностью и усложнением.

Тот же RabbitMQ использует Push-модель и при поступлении в очередь сообщения сам оповещает слушателей

любая таблица в БД с автоинкрементным ID или полем даты добавления записи - это уже очередь

Таблицы не упорядочены сами по себе. Порядок выдачи записей контролируется либо явно (например, через оператор ORDER BY, если мы говорим об SQL), либо неявно - через дефалтное поле

Это описание Pull-модели, и она не очень подходит для real-time взаимодействия. Вам надо будет опрашивать бд однотипными запросами. А это чтение с диска. И чем больше вы захотите приблизиться к real-time, тем больше придется дидосить бд.

Никаких лишних запросов к БД не происходит.

Вынимающий процесс - вынимает записи из БД пачками в цикле, пока не опустошит очередь и делает только те запросы, которые нужны для этого, никаких лишних запросов. Если очередь опустела и больше нечего вынимать - он засыпает.

Заполняющий процесс пишет в очередь тогда, когда сам не спит и как только он туда что-то записал - он пинает сигналом SIGCONT и пробуждает вынимающий процесс, который в этот момент либо спит и тогда просыпается и начинает выполнять свою работу либо уже работает и игнорирует SIGCONT.

Я об этом написал в статье - возможно вы её невнимательно прочли или неправильно поняли. Спящий процесс не потребляет ресурсов, а отправка межпроцессного SIGCONT - очень дешевая операция.

Таким образом, если бот простаивает - происходит следующее: работает только Input и только тогда когда запускает очередной цикл лонг полинга. Пока Телеграм не отдал ему ни одного входящего сообщения он тоже спит до таймаута лонг полинга. Все остальные процессы тоже спят и ничего не потребляют.

Как только Input хоть что-то получит от Телеграма - он записывает это в очередь обработки и пинает Proc. Тот в свою очередь, как только что-то запишет в очередь отправки - пинает Output. Всё настолько просто, что даже можно сказать - примитивно, и поэтому - быстро, надежно и безотказно работает!

Спасибо за объяснение и саму архитектуру, как раз такое и искал для своего бота.

Sign up to leave a comment.

Articles