## Лабораторная работа 4. Вариант 4. ### Задание Изучить проектирования приложений при помощи брокера сообщений. - Установить брокер сообщений `RabbitMQ`, - Пройти первые 3 урока из `RabbitMQ Tutorials`, - Продемонстрировать работу брокера сообщений. ### Как запустить Для запуска программы необходимо с помощью командной строки в корневой директории файлов прокета прописать: ``` python publisher.py python consumer1.py python consumer2.py ``` Результат работы программы будет выведен в консоль. ### Используемые технологии - Брокер сообщений `RabbitMQ` - программная система, реализующая протокол AMQP (Advanced Message Queuing Protocol), который представляет собой стандартный протокол обмена сообщениями между приложениями. `RabbitMQ` работает на основе модели "производитель-потребитель" (producer-consumer), где приложения, называемые "производителями", создают и отправляют сообщения в очередь, а другие приложения, называемые "потребителями", получают и обрабатывают эти сообщения из очереди. RabbitMQ обеспечивает надежную доставку сообщений, сохраняя их в очереди до тех пор, пока они не будут получены и обработаны потребителями. - Библиотека `pika`, обеспечивающая полную поддержку протокола AMQP (Advanced Message Queuing Protocol), который является стандартом для обмена сообщениями в системах очередей сообщений. Благодаря этой библиотеке возможно создание и настройка связи между компонентами системы, обмен сообщениями и управление очередями, используя простой и понятный API. ### Описание работы #### Выполнение RabbitMQ Tutorials ##### Урок "Hello World!" В данном уроке рассматриваются две небольшие программы на Python; производитель (отправитель), который отправляет одно сообщение, и потребитель (получатель), который получает сообщения "Привет, мир" и распечатывает их. ![](diag1.png "") Результат выполнения программ: ![](prog1.png "") ##### Урок "Work queues" В данном уроке рассматривается создание рабочей очереди, которая будет использоваться для распределения трудоемких задач между несколькими рабочими. ![](diag2.png "") Результат выполнения программ: ![](prog2.png "") ##### Урок "Publish/Subscribe" В данном уроке рассматривается создание простой системы ведения журнала, состоящей из двух программ - первая будет отправлять сообщения журнала, а вторая будет получать и распечатывать их. Доставка сообщений будет производиться для нескольких потребителей. Таким образом, в данном уроке рассматривается реализация шаблока "публикация / подписка". ![](diag3.png "") Результат выполнения программ: ![](prog3.png "") #### Выполнение демонстрационного приложения В качестве предметной области была выбрана тематика курсовой работы "Суши-бар". В таком случае, `publisher` раз в секунду отправляет сообщение по состоянию элементов заказа, на которое `consumer1` и `consumer2` необходимо отреагировать. ```python logs = ["get address", "get order", "get pavement", "order done"] connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() exchange_name = 'logs' channel.exchange_declare(exchange=exchange_name, exchange_type='fanout') print(' [*] Started. To exit press CTRL+C') while 1: log = random.choice(logs) channel.basic_publish(exchange=exchange_name, routing_key='', body=log) print(f" [x] Published: {log}") time.sleep(1) ``` Данные сообщение транслируются на обе очереди подписчиков: `slow-queue` и `fast-queue`. `consumer1` принимает сообшения в очередь `slow-queue` и реагирует на них (обрабатывает) в течении 2-3 секунд. ```python def message_manager(channel, queue_name, exchange_name): channel.queue_declare(queue=queue_name) channel.queue_bind(exchange=exchange_name, queue=queue_name) def callback(ch, method, properties, body): task = body.decode() print(f" [x] Received : {task}") time.sleep(random.randint(2, 3)) if task == "get address": print(" [x] Address set") elif task == "get order": print(" [x] Order sent to preparation") elif task == "get pavement": print(" [x] Bank account checked") else: print(" [x] Order sent to a delivery") ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_consume(queue=queue_name, on_message_callback=callback) print("[*] Waiting for messages. To exit press CTRL+C") channel.start_consuming() connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() exchange_name = 'logs' queue_name = 'slow-queue' consumer_thread = threading.Thread(target=message_manager, args=(channel, queue_name, exchange_name)) consumer_thread.start() consumer_thread.join() ``` `consumer2` принимает сообшения в очередь `fast-queue` и реагирует на них (обрабатывает) без задержек. ```python connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() exchange_name = 'logs' queue_name = 'fast-queue' channel.queue_declare(queue=queue_name) channel.queue_bind(exchange=exchange_name, queue=queue_name) def callback(ch, method, properties, body): task = body.decode() print(f" [x] Received : {task}") if task == "get address": print(" [x] Address set") elif task == "get order": print(" [x] Order sent to preparation") elif task == "get pavement": print(" [x] Bank account checked") else: print(" [x] Order sent to a delivery") ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_consume(queue=queue_name, on_message_callback=callback) print("[*] Waiting for messages. To exit press CTRL+C") channel.start_consuming() ``` #### Работа программы. Обзор скорости обработки сообщений В первом случае, запустим по одному экземпляру каждого приложения. Проверим состояния очередей каждого из consumer: ![](rep1.png "") Согласно данныи `RabbitMQ`, задачи в очереди `slow-queue` исполнителя `consumer1` накапливаются линейно, поскольку данный исполнитель обрабатывает задачи с задержкой. Задачи в очереди `fast-queue` исполнителя `consumer2` не накапливаются, т.к. данный исполнитель выполняет задачи мгновенно. Во втором случае, запустим исполнитель `consumer1` в 3х экземплярах и проверим состояние очереди `slow-queue`: ![](rep2.png "") Как можно заметить, в данной конфигурации удалось добиться постоянного количества задач в очереди `slow-queue` - 2-3 задачи. Это обусловлено тем, что принимаемые `consumer1` задачи равномерно распределяются по 3м исполнителям и успевают обрабатываться ими до накопления задач от `publisher`. ### Вывод Таким образом, `RabbitMQ` может иметь применение в распределённых системах в качестве узла обмена сообщениями между микросервисами для распределения и адресации задач между ними, а также обмеспечивает управление данными процессами. ### Видео https://youtu.be/T0lIQIHTenY