Files
hse-2026/homework/05-message-queue/readme.md
T
2026-10-10 11:05:32 +03:00

14 KiB
Raw Blame History

Практика с RabbitMQ

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

Архитектура и интерфейс сервиса

Очередь сообщений позволяет передать длительную задачу на асинхронную обработку. Она помогает распределять работу между несколькими воркерами и сглаживать всплески нагрузки. В этом задании вам также предстоит обеспечить обработку задач при временных отказах.

Архитектура сервиса
  1. Сервер с REST API принимает запросы пользователей и передаёт задачи на генерацию описаний через RabbitMQ.
  2. Воркеры получают задачи, генерируют описания и записывают их в общее хранилище, откуда сервер читает результаты. В задании хранилище — это Docker volume, см. docker-compose.yml.

Версия RabbitMQ для проверки задана в docker-compose.yml. Используйте это окружение для локального тестирования.

REST API

Тела запросов и успешных ответов передаются в JSON. Успешный запрос возвращает код 200; формат тела ответа при ошибке не регламентируется. Идентификатор изображения может быть строкой или целым числом; в ответах POST и GET он должен иметь одинаковый тип.

POST /api/v1.0/images
Принимает URL изображения для обработки и возвращает id изображения.
Каждый запрос получает новый уникальный id, даже при повторной отправке того же URL.
Если JSON некорректен или image_url отсутствует, пуст или не является строкой,
верните код 400.

Тело запроса:
{
    "image_url": str
}

Тело ответа:
{
    "image_id": str | int
}
GET /api/v1.0/images
Возвращает id всех обработанных изображений без повторов. Порядок не важен.

Тело ответа:
{
    "image_ids": List[str | int]
}
GET /api/v1.0/images/<image_id>
Возвращает описание для данного изображения, если оно уже было обработано, 
и код 404, если id неизвестен или обработка ещё не завершена.

Тело ответа:
{
    "caption": str
}

Компоненты задания и оценивание

За выполнение задания можно получить 10 баллов:

Группа тестов Баллы Что проверяется
BASIC_API 1 Пустой список результатов, отклонение некорректных запросов и ответ 404 для неизвестного ID.
BASIC_PROCESSING 3 Передача задач через RabbitMQ, обработка воркерами и получение результатов через REST API без отказов.
CALLBACKS 2 Уведомления о завершении обработки через RabbitMQ: сервер отвечает на GET .../images без чтения списка файлов в общей директории.
FAULT_TOLERANCE_1 1 Работа сервиса после простоя соединений с брокером.
FAULT_TOLERANCE_2 1 Сохранность запросов при недоступности и перезапуске брокера во время отправки задач.
FAULT_TOLERANCE_3 1 Завершение задач после отказа одного или обоих воркеров.
FAULT_TOLERANCE_4 1 Восстановление после отказов брокера и воркеров, сохранность уведомлений и обработка повторных доставок.

Баллы за каждую группу начисляются при прохождении всех её тестов. Провал одной группы не обнуляет баллы за остальные. Группа FAULT_TOLERANCE_4 предполагает реализацию уведомлений через RabbitMQ; передавать в них сами описания изображений не требуется.

При проверке отказоустойчивости уже запущенный REST API должен принимать корректные запросы с кодом 200 и возвращать готовые результаты, даже когда брокер или воркеры недоступны. Запрос считается принятым, если POST вернул 200. После восстановления брокера, связи и хотя бы одного воркера каждый принятый запрос должен быть обработан, а его описание — доступно через API. Таймаут каждого HTTP-запроса в тестах — 3 секунды.

Сервер и общее хранилище в этих сценариях не отказывают: восстановление после их перезапуска или потери данных не требуется. Повторная доставка задач и уведомлений возможна. Повторная обработка задачи допустима, но её ID должен появляться в списке результатов только один раз.

Приложите отчёт solution/readme.md с описанием устройства решения и выполненных компонентов. Для отказоустойчивости обоснуйте, почему принятые запросы не теряются. На защите нужно объяснить работу реализованных компонентов и подтвердить это обоснование. Общие требования к отчёту приведены в правилах курса.

Заготовки для решения

В папке solution/server находится заготовка для сервера с реализацией REST API на Flask.

В папке solution/worker содержится заготовка для воркера. Для генерации описания используйте функцию produce_image_caption, передавая ей строку image_url. Скачивать и сохранять само изображение не нужно: функция имитирует его обработку. В файл сохраняется только полученное описание в кодировке UTF-8.

Очередь задач должна называться task_queue, а описания должны сохраняться в файлы /data/{image_id}.txt. Эти соглашения используются в тестах.

Для взаимодействия с RabbitMQ на Python предлагается использовать библиотеку Pika (см. семинар 5).

Весь код решения должен размещаться в папке solution. При сдаче решения в тестирующую систему отправляется только эта папка, изменения вне неё учитываться не будут.

Порядок выполнения задания

Начните с материалов семинара 5 и примера work_queues. Дополнительно можно обращаться к официальным туториалам RabbitMQ.

  1. Реализуйте передачу задач от REST API через RabbitMQ, обработку воркерами и получение результатов. Проверьте группы BASIC_API и BASIC_PROCESSING. При необходимости измените Dockerfile сервера и воркера.
  2. Добавьте уведомления сервера о завершении обработки и проверьте группу CALLBACKS. Полезный пример обмена запросами и ответами есть в туториале по RPC. При использовании Pika учитывайте ограничения потокобезопасности.
  3. Работу над отказоустойчивостью начните с test_heartbeats_timeout (FAULT_TOLERANCE_1). Изучите документацию по heartbeat и настройки в rabbitmq.conf. Для BlockingConnection полезно обсуждение работы соединения при редкой отправке сообщений.
  4. Подумайте, в каких случаях ваше решение может потерять принятый запрос. Изучите подтверждения доставки в RabbitMQ; для Pika также полезен пример асинхронного отправителя. Проверьте отдельно отказы брокера при отправке задач (FAULT_TOLERANCE_2) и отказы воркеров (FAULT_TOLERANCE_3).
  5. Перейдите к комплексным сценариям FAULT_TOLERANCE_4: перезапуску брокера, сохранности уведомлений и повторной доставке. Сопоставьте поведение решения с руководством RabbitMQ по надёжности. Подумайте, какие моменты отказа ещё стоит проверить самостоятельно.
  6. Подготовьте отчёт с обоснованием отказоустойчивости реализованных компонентов, запустите все тесты и сдайте решение с отчётом в тестирующую систему.

При отладке сопоставляйте логи компонентов с состоянием очередей в веб-интерфейсе RabbitMQ: http://localhost:15672, логин и пароль — guest. Как на семинаре, обращайте внимание на сообщения Ready и Unacked.

Тестирование решения

Тесты, проверяющие решение, находятся в папке tests.

Бонусы за пробелы в тестах начисляются по общим правилам.

Локальное тестирование

Команды ниже выполняйте из папки задания. Для локального запуска тестов установите зависимости из tests/requirements.txt.

Перед запуском тестов соберите образы:

docker compose build

Запуск всех тестов выполняется с помощью команды:

python3 tests/main.py

Отдельный тест можно запустить так:

pytest -vs --tb=short tests/test_server.py::test_single_image

Для тестирования в окружении, аналогичном тестирующей системе, выполните из папки задания:

docker run --privileged --pull always --rm -v ./solution:/hw/solution distsys.ru/course/message-queue:latest

Проверка в тестирующей системе

Отправьте ваше решение в тестирующую систему следуя инструкции и дождитесь результатов.

ЧаВо

Можно ли реализовать решение не на Python?

Да. Замените заготовки и Dockerfile сервера и воркера. Реализуйте аналог функции-заглушки, возвращающий строку по image_url; точное совпадение результата с Python-версией не требуется.

Можно ли обрабатывать изображения локально на сервере?

Нет. Описания должны генерироваться только воркерами. Решение, которое генерирует их на сервере, не засчитывается (0 баллов).