14 KiB
Практика с RabbitMQ
В этом задании вы научитесь работать с брокером сообщений RabbitMQ. Вам предстоит реализовать сервис, который принимает URL изображения, асинхронно генерирует описание, сохраняет его в файл и возвращает по запросу. Генерация описания имитируется готовой функцией из заготовки.
Архитектура и интерфейс сервиса
Очередь сообщений позволяет передать длительную задачу на асинхронную обработку. Она помогает распределять работу между несколькими воркерами и сглаживать всплески нагрузки. В этом задании вам также предстоит обеспечить обработку задач при временных отказах.
- Сервер с REST API принимает запросы пользователей и передаёт задачи на генерацию описаний через RabbitMQ.
- Воркеры получают задачи, генерируют описания и записывают их в общее хранилище, откуда сервер читает результаты. В задании хранилище — это 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.
- Реализуйте передачу задач от REST API через RabbitMQ, обработку воркерами и получение результатов. Проверьте группы
BASIC_APIиBASIC_PROCESSING. При необходимости измените Dockerfile сервера и воркера. - Добавьте уведомления сервера о завершении обработки и проверьте группу
CALLBACKS. Полезный пример обмена запросами и ответами есть в туториале по RPC. При использовании Pika учитывайте ограничения потокобезопасности. - Работу над отказоустойчивостью начните с
test_heartbeats_timeout(FAULT_TOLERANCE_1). Изучите документацию по heartbeat и настройки в rabbitmq.conf. ДляBlockingConnectionполезно обсуждение работы соединения при редкой отправке сообщений. - Подумайте, в каких случаях ваше решение может потерять принятый запрос. Изучите подтверждения доставки в RabbitMQ; для Pika также полезен пример асинхронного отправителя. Проверьте отдельно отказы брокера при отправке задач (
FAULT_TOLERANCE_2) и отказы воркеров (FAULT_TOLERANCE_3). - Перейдите к комплексным сценариям
FAULT_TOLERANCE_4: перезапуску брокера, сохранности уведомлений и повторной доставке. Сопоставьте поведение решения с руководством RabbitMQ по надёжности. Подумайте, какие моменты отказа ещё стоит проверить самостоятельно. - Подготовьте отчёт с обоснованием отказоустойчивости реализованных компонентов, запустите все тесты и сдайте решение с отчётом в тестирующую систему.
При отладке сопоставляйте логи компонентов с состоянием очередей в веб-интерфейсе 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 баллов).