我有一个Python Web应用程序,客户端(Ember.js)通过WebSocket与服务器通信(我正在使用Flask-SocketIO)。 除了WebSocket服务器之外,后端还有两件值得提及的事情:
当客户端提交图像时,将在数据库中创建其实体,并将id放入图像转换队列中。工人抓住它并进行图像转换。之后,工作人员将其放入OCR队列,在那里它将由OCR队列工作者处理。
到目前为止一切顺利。 WS请求在不同的线程中同步处理(Flask-SocketIO使用Eventlet),并且繁重的计算操作异步发生(在单独的线程中也是如此)。
现在问题是:整个应用程序运行在 Raspberry Pi 3 上。如果我没有使用它的4核,我只有一个主频为1.2 GHz的ARMv8核心。这对于OCR来说是非常小的力量。所以我决定了解如何在Python中使用多个内核。虽然我读到了GIL的问题,但我发现了multiprocessing所说的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的队列和线程库是monkey patched。如果我停止使用Monkey修补版本的Thread和Queue并使用multiprocsssing
中的那个,那么在访问队列时,Eventlet启动的请求线程会永久阻塞。
现在我的问题:
有什么方法可以让这个应用程序在单独的核心上进行OCR和图像转换?
如果可能的话,我想继续使用WebSocket和Eventlet。我的优点是进程之间唯一的通信接口是队列。
我已经拥有的想法: - 不使用队列的Python实现,而是使用I / O.例如,不同子进程可以访问的专用Redis - 更进一步:将每个队列工作者作为单独的Python进程启动(例如python3 wsserver | python3 ocrqueue | python3 imgconvqueue)。然后我必须确保自己对队列和数据库的访问是非阻塞的
最好的办法是保持单个流程并使其与多处理协同工作。
非常感谢您提前
答案 0 :(得分:4)
Eventlet当前与多处理程序包不兼容。这项工作有一个未解决的问题:https://github.com/eventlet/eventlet/issues/210。
我认为在您的情况下运行良好的替代方法是使用Celery来管理您的队列。 Celery将启动一个工作进程池,等待主进程通过消息队列提供的任务(同时支持RabbitMQ和Redis)。
Celery工作者不需要使用eventlet,只需要使用主服务器,这样就可以让他们在没有eventlet强加的限制的情况下做任何他们需要做的事情。
如果您有兴趣探索这种方法,我有一个使用它的完整示例:https://github.com/miguelgrinberg/flack。