159 lines
9.6 KiB
Markdown
159 lines
9.6 KiB
Markdown
|
||
## Лабораторная работа 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; производитель (отправитель), который отправляет одно сообщение, и потребитель (получатель), который получает сообщения "Привет, мир" и распечатывает их.
|
||
|
||

|
||
|
||
Результат выполнения программ:
|
||

|
||
|
||
##### Урок "Work queues"
|
||
В данном уроке рассматривается создание рабочей очереди, которая будет использоваться для распределения трудоемких задач между несколькими рабочими.
|
||
|
||

|
||
|
||
Результат выполнения программ:
|
||

|
||
|
||
##### Урок "Publish/Subscribe"
|
||
В данном уроке рассматривается создание простой системы ведения журнала, состоящей из двух программ - первая будет отправлять сообщения журнала, а вторая будет получать и распечатывать их. Доставка сообщений будет производиться для нескольких потребителей.
|
||
|
||
Таким образом, в данном уроке рассматривается реализация шаблока "публикация / подписка".
|
||
|
||

|
||
|
||
Результат выполнения программ:
|
||

|
||
|
||
#### Выполнение демонстрационного приложения
|
||
|
||
В качестве предметной области была выбрана тематика курсовой работы "Суши-бар".
|
||
|
||
В таком случае, `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:
|
||
|
||

|
||
|
||
Согласно данныи `RabbitMQ`, задачи в очереди `slow-queue` исполнителя `consumer1` накапливаются линейно, поскольку данный исполнитель обрабатывает задачи с задержкой. Задачи в очереди `fast-queue` исполнителя `consumer2` не накапливаются, т.к. данный исполнитель выполняет задачи мгновенно.
|
||
|
||
Во втором случае, запустим исполнитель `consumer1` в 3х экземплярах и проверим состояние очереди `slow-queue`:
|
||
|
||

|
||
|
||
Как можно заметить, в данной конфигурации удалось добиться постоянного количества задач в очереди `slow-queue` - 2-3 задачи. Это обусловлено тем, что принимаемые `consumer1` задачи равномерно распределяются по 3м исполнителям и успевают обрабатываться ими до накопления задач от `publisher`.
|
||
|
||
### Вывод
|
||
Таким образом, `RabbitMQ` может иметь применение в распределённых системах в качестве узла обмена сообщениями между микросервисами для распределения и адресации задач между ними, а также обмеспечивает управление данными процессами.
|
||
|
||
### Видео
|
||
https://youtu.be/T0lIQIHTenY |