Asyncio Queue: резервная очередь при перегрузке основного конвейера
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Asyncio Queue, Lock и Semaphore: задачи на очереди и воркеров.
Диспетчер перегрузки телеметрии
Телеметрический шлюз принимает непрерывный поток входящих кадров и направляет их в основную очередь ограниченной ёмкости, из которой данные забирает пул первичных обработчиков. Если эта очередь оказывается заполненной, шлюз не задерживает поставщика данных, а сразу перенаправляет лишний кадр во вторичную резервную очередь, закреплённую за отдельным пулом дежурных воркеров.
Оба пула отправляют данные через общий сетевой канал, доступ к которому ограничен единым семафором пропускной способности. При успешной передаче кадров каждый контур обновляет собственную статистику отправлений под защитой общего замка. Система продолжает работу до тех пор, пока обе очереди полностью не подтвердят завершение обработки всех распределённых пакетов.
Реализуйте асинхронный диспетчер перегрузки и определите итоговые значения раздельных счётчиков успешно отправленных кадров для основного и резервного контуров.
Что уже дано
В среде выполнения доступен класс TelemetryContext со следующими атрибутами и методами:
context.receive()— асинхронный метод получения очередного кадра; возвращает объект кадра или маркер завершенияTelemetryContext.END;context.send(frame, route)— асинхронный метод отправки кадра, гдеrouteпринимает значение'primary'или'reserve'; метод не выбрасывает ошибок;context.main_queue— ограниченная очередьasyncio.Queueс вместимостью от 1 до 100 элементов;context.reserve_queue— неограниченная очередьasyncio.Queue;context.sem— общий семафорasyncio.Semaphore, ограничивающий суммарное число одновременных вызововsend(значение от 1 до 20);context.lock— общий замокasyncio.Lockдля синхронизации записи статистики;context.stats.record(route)— метод фиксации успешной отправки кадра для маршрута'primary'или'reserve';context.pool.start(coroutine, lane)— запускает воркер в пуле задач и регистрирует за ним контур'primary'или'reserve';context.pool.stop()— останавливает зарегистрированные задачи пула.
Вам предстоит реализовать асинхронную функцию route_overflow.
Что нужно сделать
Напишите асинхронную функцию route_overflow(context, main_workers, reserve_workers).
Параметры main_workers и reserve_workers задают количество воркеров для основного и резервного контуров соответственно (целые положительные числа от 1 до 20). Общее число входящих кадров составляет от 0 до 1000.
Функция должна вернуть объект, совместимый с collections.abc.Mapping (например, словарь), со строковыми ключами 'primary' и 'reserve' и целочисленными количествами успешно отправленных кадров по каждому контуру.
Правила диспетчеризации и обработки:
- Запустите через
context.pool.start(..., lane='primary')ровноmain_workersворкеров, читающихcontext.main_queue, и черезcontext.pool.start(..., lane='reserve')ровноreserve_workersворкеров, читающихcontext.reserve_queue. - Непрерывно считывайте кадры через
await context.receive(). Для каждого кадра попытайтесь немедленно положить его в основную очередь черезcontext.main_queue.put_nowait(frame). - Если основная очередь заполнена (возникает
asyncio.QueueFull), сразу поместите этот же кадр вcontext.reserve_queue. Не ждите освобождения места в основной очереди, не сбрасывайте кадры и не заменяйте их. - Воркеры каждого контура извлекают кадры из своей очереди. Для каждого извлечённого кадра воркер выполняет
await context.send(frame, route), удерживая общий семафорcontext.sem. - После успешной отправки воркер захватывает общий замок
context.lockи вызываетcontext.stats.record(route). - Для каждого извлечённого из очереди кадра должен быть гарантированно вызван
queue.task_done()соответствующей очереди. - Когда метод
receive()возвращаетTelemetryContext.END, новые кадры больше не поступают. Диспетчер должен дождаться полной обработки всех кадров черезawait context.main_queue.join()иawait context.reserve_queue.join(), после чего вызватьawait context.pool.stop(). - Итоговые значения в возвращаемом словаре должны совпадать со значениями в
context.stats.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня route_overflow и вызывает её с проверочными аргументами.
Пример использования
import asyncio
items = iter(range(1))
async def receive():
return next(items, TelemetryContext.END)
async def send(frame, route):
await asyncio.sleep(0)
context = TelemetryContext(receive, send, 2, 2)
print(asyncio.run(route_overflow(context, 2, 1)))
Вывод:
{'primary': 1, 'reserve': 0}
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Race condition в asyncio: почему однопоточность не защищает счётчик
- Asyncio Queue и Semaphore: конвейер с ограничением нагрузки
- Asyncio Queue: вытеснение старейшей заявки без блокировки поставщика
- Asyncio Lock и Semaphore: учёт результатов обработки контейнеров
- Asyncio: восстановление пула воркеров после ошибки
- Asyncio Queue: объединение парных событий по идентификатору
