![](/img/trans.png)
[英]Setting up periodic tasks in Celery (celerybeat) dynamically using add_periodic_task
[英]Celery add_periodic_task blocks Django running in uwsgi environment
我編寫了一個模塊,根據項目設置中的字典列表(通過django.conf.settings
導入)動態添加定期芹菜任務。 我使用add_tasks
函數來執行此操作,該函數調度要使用設置中給出的特定uuid
調用的函數:
def add_tasks(celery):
for new_task in settings.NEW_TASKS:
celery.add_periodic_task(
new_task['interval'],
my_task.s(new_task['uuid']),
name='My Task %s' % new_task['uuid'],
)
建議一樣在這里我用on_after_configure.connect
信號調用的函數在我celery.py
:
app = Celery('my_app')
@app.on_after_configure.connect
def setup_periodic_tasks(celery, **kwargs):
from add_tasks_module import add_tasks
add_tasks(celery)
這個設置適用於celery beat
和celery worker
但在我使用uwsgi
服務我的django應用程序的地方打破了我的設置。 Uwsgi
運行順利,直到視圖代碼第一次使用celery的.delay()
方法發送任務。 在這一點上,似乎芹菜在uwsgi
初始化,但在上面的代碼中永遠阻止。 如果我從命令行手動運行它然后在它阻塞時中斷,我得到以下(縮短的)堆棧跟蹤:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/kombu/utils/objects.py", line 42, in __get__
return obj.__dict__[self.__name__]
KeyError: 'tasks'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/kombu/utils/objects.py", line 42, in __get__
return obj.__dict__[self.__name__]
KeyError: 'data'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/kombu/utils/objects.py", line 42, in __get__
return obj.__dict__[self.__name__]
KeyError: 'tasks'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
(SHORTENED HERE. Just contained the trace from the console through my call to this function)
File "/opt/my_app/add_tasks_module/__init__.py", line 42, in add_tasks
my_task.s(new_task['uuid']),
File "/usr/local/lib/python3.6/site-packages/celery/local.py", line 146, in __getattr__
return getattr(self._get_current_object(), name)
File "/usr/local/lib/python3.6/site-packages/celery/local.py", line 109, in _get_current_object
return loc(*self.__args, **self.__kwargs)
File "/usr/local/lib/python3.6/site-packages/celery/app/__init__.py", line 72, in task_by_cons
return app.tasks[
File "/usr/local/lib/python3.6/site-packages/kombu/utils/objects.py", line 44, in __get__
value = obj.__dict__[self.__name__] = self.__get(obj)
File "/usr/local/lib/python3.6/site-packages/celery/app/base.py", line 1228, in tasks
self.finalize(auto=True)
File "/usr/local/lib/python3.6/site-packages/celery/app/base.py", line 507, in finalize
with self._finalize_mutex:
獲取互斥鎖似乎存在問題。
目前我正在使用一種解決方法來檢測sys.argv[0]
包含uwsgi
然后不添加周期性任務,因為只有beat
需要任務,但我想了解這里出了什么問題來更永久地解決問題。
這個問題可能與使用uwsgi多線程或多處理有關,其中一個線程/進程持有另一個需要的互斥鎖嗎?
我很感激可以幫助我解決問題的任何提示。 謝謝。
我正在使用:Django 1.11.7和Celery 4.1.0
編輯1
我已經為這個問題創建了一個最小的設置:
celery.py:
import os
from celery import Celery
from django.conf import settings
from myapp.tasks import my_task
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_app.settings')
app = Celery('my_app')
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
sender.add_periodic_task(
60,
my_task.s(),
name='Testtask'
)
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
tasks.py:
from celery import shared_task
@shared_task()
def my_task():
print('ran')
確保CELERY_TASK_ALWAYS_EAGER = False,並且您有一個有效的消息隊列。
跑:
./manage.py shell -c 'from myapp.tasks import my_task; my_task.delay()'
在中斷前等待大約10秒鍾以查看上述錯誤。
所以,我發現@shared_task
裝飾器會產生問題。 當我在信號調用的函數中聲明任務時,我可以避免這個問題:
def add_tasks(celery):
@celery.task
def my_task(uuid):
print(uuid)
for new_task in settings.NEW_TASKS:
celery.add_periodic_task(
new_task['interval'],
my_task.s(new_task['uuid']),
name='My Task %s' % new_task['uuid'],
)
這個解決方案實際上對我有用,但我還有一個問題:我在一個可插拔的應用程序中使用這個代碼,所以我不能直接訪問信號處理程序之外的芹菜應用程序但是也想能夠調用其他代碼中的my_task
函數。 通過在函數中定義它,它在函數外部不可用,所以我無法在其他地方導入它。
我可以通過在信號函數之外定義任務函數來解決這個問題,並在這里和tasks.py
使用不同的裝飾器。 我想知道是否除了@shared_task
裝飾器之外還有一個裝飾器,我可以在不會產生問題的tasks.py
中使用它。
目前最好的解決方案可能是:
task_app.__init__.py:
def my_task(uuid):
# do stuff
print(uuid)
def add_tasks(celery):
celery_my_task = celery.task(my_task)
for new_task in settings.NEW_TASKS:
celery.add_periodic_task(
new_task['interval'],
celery_my_task(new_task['uuid']),
name='My Task %s' % new_task['uuid'],
)
task_app.tasks.py:
from celery import shared_task
from task_app import my_task
shared_my_task = shared_task(my_task)
myapp.celery.py:
import os
from celery import Celery
from django.conf import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_app.settings')
app = Celery('my_app')
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
from task_app import add_tasks
add_tasks(sender)
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
你可以嘗試發信號@app.on_after_finalize.connect
:
來自工作項目celery==4.1.0
一些快速片段celery==4.1.0
, Django==2.0
, django-celery-beat==1.1.0
和django-celery-results==1.0.1
@app.on_after_finalize.connect
def setup_periodic_tasks(sender, **kwargs):
""" setup of periodic task :py:func:shopify_data_fetcher.celery.fetch_shopify
based on the schedule defined in: settings.CELERY_BEAT_SCHEDULE
"""
for task_name, task_config in settings.CELERY_BEAT_SCHEDULE.items():
sender.add_periodic_task(
task_config['schedule'],
fetch_shopify.s(**task_config['kwargs']['resource_name']),
name=task_name
)
一塊CELERY_BEAT_SCHEDULE
:
CELERY_BEAT_SCHEDULE = {
'fetch_shopify_orders': {
'task': 'shopify.tasks.fetch_shopify',
'schedule': crontab(hour="*/3", minute=0),
'kwargs': {
'resource_name': shopify_constants.SHOPIFY_API_RESOURCES_ORDERS
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.