[英]Celery Worker Database Connection Pooling
我正在使用 Celery 獨立(不在 Django 中)。 我計划在多台物理機器上運行一種工作任務類型。 該任務執行以下操作
我正在使用 PostgreSQL,但這同樣適用於使用連接的其他存儲類型。 過去,我使用數據庫連接池來避免在每個請求上創建新的數據庫連接或避免連接打開時間過長。 但是,由於每個 Celery 工人都在一個單獨的進程中運行,我不確定他們實際上如何能夠共享池。 我錯過了什么嗎? 我知道 Celery 允許您保留從 Celery 工人返回的結果,但這不是我在這里想要做的。 每個任務都可以根據處理的數據進行多次不同的更新或插入。
從 Celery 工作者內部訪問數據庫的正確方法是什么?
是否可以在多個工作人員/任務之間共享一個池,或者有其他方法可以做到這一點?
我喜歡 Tigeronk2 的每個工人一個連接的想法。 正如他所說,Celery 維護着自己的工作池,因此確實不需要單獨的數據庫連接池。 Celery Signal 文檔解釋了如何在創建 worker 時進行自定義初始化,因此我將以下代碼添加到我的 tasks.py 中,它似乎完全按照您的預期工作。 當工作人員關閉時,我什至能夠關閉連接:
from celery.signals import worker_process_init, worker_process_shutdown
db_conn = None
@worker_process_init.connect
def init_worker(**kwargs):
global db_conn
print('Initializing database connection for worker.')
db_conn = db.connect(DB_CONNECT_STRING)
@worker_process_shutdown.connect
def shutdown_worker(**kwargs):
global db_conn
if db_conn:
print('Closing database connectionn for worker.')
db_conn.close()
每個工作進程有一個數據庫連接。 由於 celery 本身維護一個工作進程池,因此您的數據庫連接將始終等於 celery 工作人員的數量。 另一方面,它會將數據庫連接池綁定到 celery 工作進程管理。 但這應該沒問題,因為 GIL 在一個進程中一次只允許一個線程。
您可以覆蓋默認行為以在 celery 配置中使用線程工作者而不是每個進程的工作者:
CELERYD_POOL = "celery.concurrency.threads.TaskPool"
然后,您可以將共享池實例存儲在您的任務實例上,並從每個線程任務調用中引用它。
或許, celery.concurrency.gevent可以提供池共享而不加重 GIL。 但是,它的支持仍然是“實驗性的”。
還有一個psycopg2.pool.SimpleConnectionPool在 greenlets(協程)之間共享,它們都將在單個進程/線程中運行。
關於該主題的其他堆棧討論的一點點。
通過實施和監控來回饋我的發現。
歡迎反饋。
參考:使用池化http://www.prschmid.com/2013/04/using-sqlalchemy-with-celery-tasks.html
每個工作進程(由 -ck 指定的 prefork 模式)將與 DB 建立一個新連接,而無需池化或重用。 因此,如果使用池化,則只能在每個工作進程級別看到池。 所以池大小 > 1 沒有用,但重用連接仍然可以從打開和關閉中保存連接。
如果每個工作進程使用一個連接,則在初始化階段每個工作進程建立 1 個 DB 連接(prefork 模式 celery -A app worker -ck)。 它節省了反復打開和關閉的連接。
不管有多少工作線程(eventlet),每個工作線程(celery -A app worker -P eventlet)只建立一個到DB的連接,沒有池化或復用。 因此,對於 eventlet,一個 celery 進程(celery -A app worker ...)上的所有工作線程(eventlets)在每一時刻都有 1 個 db 連接。
根據芹菜文檔
但是您需要確保您的任務不會執行阻塞調用,因為這將停止工作器中的所有其他操作,直到阻塞調用返回。
這可能是由於 MYSQL DB 連接的方式阻塞了調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.