Использование нескольких ядер с Python и Eventlet
У меня есть веб-приложение Python, в котором клиент ( Ember.js) связывается с сервером через WebSocket (я использую Flask-SocketIO). Помимо сервера WebSocket серверная часть делает еще две вещи, о которых стоит упомянуть:
- Делать некоторые преобразования изображения (используя http://www.graphicsmagick.org/)
- OCR входящие изображения с клиента (с использованием tesseract)
Когда клиент отправляет изображение, его сущность создается в базе данных, а идентификатор помещается в очередь преобразования изображения. Работник хватает его и делает преобразование изображения. После этого работник помещает его в очередь OCR, где он будет обрабатываться работником очереди OCR.
Все идет нормально. Запросы WS обрабатываются синхронно в отдельных потоках (для этого Flask-SocketIO использует Eventlet), а тяжелое вычислительное действие происходит асинхронно (также в отдельных потоках).
Теперь проблема: все приложение работает на Raspberry Pi 3. Если я не использую 4 ядра, у меня есть только одно ядро ARMv8 с тактовой частотой 1,2 ГГц. Это очень мало для распознавания текста. Поэтому я решил выяснить, как использовать несколько ядер с Python. Хотя я читал о проблемах с GIL) я узнал о многопроцессорности, где говорится The multiprocessing package offers both local and remote concurrency, effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads.
, Именно то, что я хотел. Поэтому я сразу заменил
from threading import Thread
thread = Thread(target=heavy_computational_worker_thread)
thread.start()
от
from multiprocessing import Process
process = Process(target=heavy_computational_worker_thread)
process.start()
Очередь также должна была обрабатываться несколькими ядрами, поэтому мне пришлось изменить
from queue import Queue
queue = multiprocessing.Queue()
в
import multiprocessing
queue = multiprocessing.Queue()
также. Проблема: очереди и библиотеки потоков обезьяны, исправлены Eventlet. Если я перестану использовать версию Thread and Queue с исправленной обезьяной и использую multiprocsssing
вместо этого поток запроса, запущенный Eventlet, блокируется навсегда при доступе к очереди.
Теперь мой вопрос:
Можно ли как-нибудь сделать так, чтобы это приложение делало OCR и преобразование изображений на отдельном ядре?
Я хотел бы продолжать использовать WebSocket и Eventlet, если это возможно. У меня есть преимущество в том, что единственным коммуникационным интерфейсом между процессами будет очередь.
У меня уже были идеи: - Не использовать реализацию очереди в Python, а использовать ввод-вывод. Например, выделенный Redis, к которому будут обращаться разные подпроцессы. Идем дальше: запускаем каждого работника очереди как отдельный процесс Python (например, python3 wsserver | python3 ocrqueue | python3 imgconvqueue). Тогда я должен был бы убедиться, что доступ к очереди и базе данных будет неблокирующим
Лучше всего было бы сохранить единый процесс и заставить его работать с многопроцессорностью.
заранее большое спасибо
1 ответ
Eventlet в настоящее время несовместим с многопроцессорным пакетом. Существует открытый вопрос для этой работы: https://github.com/eventlet/eventlet/issues/210.
Я думаю, что альтернатива, которая будет работать в вашем случае, - это использовать Celery для управления вашей очередью. Celery запустит пул рабочих процессов, которые ожидают выполнения задач, предоставленных основным процессом через очередь сообщений (поддерживаются RabbitMQ и Redis).
Сотрудникам Celery не нужно использовать eventlet, только основной сервер, поэтому это позволяет им делать все, что им нужно, без ограничений, накладываемых eventlet.
Если вы заинтересованы в изучении этого подхода, у меня есть полный пример, который использует его: https://github.com/miguelgrinberg/flack.