3

我有一個Python web應用程序,客戶端(Ember.js)通過WebSocket與服務器通信(我使用的是Flask-SocketIO)。 除了網頁套接字服務器後端做兩兩件事,值得一提的:在Python和Eventlet中使用多核心

當客戶端提交圖像時,其實體將在數據庫中創建,並將該ID放入圖像轉換隊列中。工人抓住它並進行圖像轉換。之後,工作人員將其放入OCR隊列中,由OCR隊列工作人員處理。

到目前爲止這麼好。 WS請求在單獨的線程中同步處理(Flask-SocketIO爲此使用了Eventlet)並且繁重的計算操作異步發生(在單獨的線程中)。

現在的問題:整個應用程序運行在Raspberry Pi 3。如果我沒有使用4個內核,我只有一個ARMv8內核的主頻爲1.2 GHz。這對於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 multiprocessing import Process 
process = Process(target=heavy_computational_worker_thread) 
process.start() 

需要進行,所以我不得不改變

from queue import Queue 
queue = multiprocessing.Queue() 

import multiprocessing 
queue = multiprocessing.Queue() 
由多個內核以及處理的隊列取代了

from threading import Thread 
thread = Thread(target=heavy_computational_worker_thread) 
thread.start() 

。有問題:Eventlet的隊列和線程庫是monkey patched。如果我停止使用線程和隊列的Monkey修補版本並使用multiprocsssing中的那個版本,則在訪問隊列時,由Eventlet啓動的請求線程永遠阻塞。

現在我的問題:

有什麼辦法可以讓這個應用程序做一個單獨的核心OCR和圖像轉換?

我想繼續使用WebSocket和Eventlet(如果可能的話)。我擁有的優勢是進程之間的唯一通信接口就是隊列。

我已經有的想法: - 不使用隊列的Python實現,而是使用I/O。例如,不同子進程可以訪問的專用Redis - 更進一步:將每個隊列工作者作爲單獨的Python進程啓動(例如,python3 wsserver | python3 ocrqueue | python3 imgconvqueue)。然後,我必須確保自己的隊列和數據庫上的訪問將是非阻塞的

儘管如此,最好的辦法是保持單個進程並使其與多處理工作。

非常感謝你提前

+0

我不知道Evenlet庫,所以也許我的答案不適用。在「主」程序中使用多線程。在主應用程序的每個「Process」中,使用「subprocess」調用子程序(即使這看起來很奇怪,也可以調用python程序來調用python程序)。僅在這些子流程中使用「Eventlet」庫(或任何非流程安全庫)。不要在主程序中使用它們。您將無法使用「隊列」,但可以通過文件傳遞數據(例如:程序A寫入圖像文件然後終止,程序B啓動並讀取此文件)。 –

回答

3

Eventlet目前與多包不兼容。這項工作有一個公開的問題:https://github.com/eventlet/eventlet/issues/210

我認爲在您的情況下可以很好地工作的替代方案是使用Celery來管理您的隊列。 Celery將啓動一個工作進程池,等待主進程通過消息隊列提供的任務(RabbitMQ和Redis都支持)。

芹菜工作人員不需要使用eventlet,只有主服務器會這樣做,所以這可以讓他們在不受eventlet限制的情況下做任何他們需要做的事情。

如果您有興趣探索這種方法,我有一個完整的示例使用它:https://github.com/miguelgrinberg/flack

+0

謝謝,這個作品很有魅力。我現在面臨的唯一問題是我在一個全局變量中擁有socketio實例,當然這個變量不能在多個進程間共享。但是這應該可以通過使用專用存儲來共享它或將變量賦予任務功能來解決。 – Schnodderbalken

+0

請參閱我在答案中引用的項目。如果您需要從工作進程發出,則在每個進程中創建一個socketio實例。不同之處在於芹菜工作者中的socketio實例不會有與它們關聯的Flask應用程序實例,因此它們僅被髮射。您不需要在進程之間共享任何內容,所有通信都通過消息隊列完成。 – Miguel

+0

我按照您在https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/docs/index.rst中記錄的說明 - 使用帶有message_queue ='redis://'的構造函數作爲參數解決了問題:)這樣我仍然只有一個socketio實例允許我從工作進程發出。 – Schnodderbalken