一、celery的安装、使用
1.celery的安装、配置
对于django的版本,我们这里默认使用最新版本(3.2.6
),并且通过虚拟环境的形式实现,虚拟环境搭建链接:https://blog.csdn.net/weixin_45859193/article/details/115408555。
在安装celery之前我们需要安装一下redis,因为celery模块需要用到队列所以我们需要redis,安装完毕后在写入到配置文件中,redis的安装链接:https://pythonav.com/wiki/detail/10/82/。
安装celery
pip3 install celery
配置redis以及celery的相关配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379", # 安装redis的主机的 IP 和 端口
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 1000,
"encoding": 'utf-8'
},
"PASSWORD": "redis密码" # redis有密码就写没有就注释掉
}
}
}
CELERY_BROKER_URL = "redis://:127.0.0.1:6379"
# CELERY_BROKER_URL = "redis://:密码@192.168.0.102:6379"
CELERY_ACCEPT_CONTENT = ['json']
CELERY_RESULT_BACKEND = "redis://:127.0.0.1:6379"
# CELERY_RESULT_BACKEND = "redis://:密码@192.168.0.102:6379"
CELERY_TASK_SERIALIZER = 'json'
在__init__.py中创建celery别名如下:
from .celery import app as celery_app
__all__ = ('celery_app',)
该方法用于在控制台启动worker的时候需要应用django。
配置完毕后创建(settings.py同级目录下
)celery.py如下:
import os
from celery import Celery
# 调用celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', "项目.settings")
app = Celery('项目名')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
对于Windows而言,我们需要安装pip install eventlet(如果执行没保存可以不装
),如果在linux中则不需要。
那么讲到这了,我们就再说一个有时候项目中会用到的操作,我们创建一个项目python manage.py startapp test
,然后在该创建项目的apps.py上我们可以通过重写ready函数,从而实现django启动时加载文件(比如一些爬虫操作、或定时任务等…
)。
test/apps.py如下:
from django.apps import AppConfig
class TestsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'test'
# 启动时加载文件
def ready(self):
super().ready()
from django.utils.module_loading import autodiscover_modules
# 文件reptile.py上执行相应操作
autodiscover_modules('reptile')
然后我们创建一个tasks.py用于写入定时任务函数如下:
from celery import shared_task
@shared_task
def x1(x, y):
return x + y
@shared_task
def x2(x, y):
return x * y
一切完毕后后我们在搭建了django虚拟环境的控制台输入celery -A 你的项目名 worker --concurrency=4 --loglevel=INFO -P threads(Windows下运行celery的命令,也就是说要指定是多线程的
)
如果看到是如上述图片则安装成功了,我们定义的两个tasks的定时任务先显示出来了。
2.celery的使用
那么我们启动完毕后该怎么使用celery呢?我们需要先创建一个视图函数,然后在调用定时任务。
所以我们在test/view.py定义视图函数如下:
from django.shortcuts import render, HttpResponse
from django.views import View
from tests import tasks
class TestView(View):
def get(self, request, *args, **kwargs):
result = tasks.x1.delay(2, 2)
return HttpResponse(result.id)
urls.py如下:
from django.urls import path
from tests import views
urlpatterns = [
path('test/', views.TestView.as_view()),
]
此时我们启动django,然后访问http://127.0.0.1:8000/test/如下:
此时任务已经加载完毕,并且将id打印出来了,那么如何获取该任务的结果呢?我们可以通过任务的id去获取结果,而获取方法我们通过在写一个路由来获取如下:
urls.py如下:
from django.urls import path
from tests import views
urlpatterns = [
path('test/', views.TestView.as_view()),
path('get/test/', views.GetTestView.as_view()),
]
view.py如下:
from django.shortcuts import render, HttpResponse
from django.views import View
from tests import tasks
from celery.result import AsyncResult
from test import celery_app
class TestView(View):
def get(self, request, *args, **kwargs):
result = tasks.x1.delay(2, 2)
return HttpResponse(result.id)
class GetTestView(View):
def get(self, request, *args, **kwargs):
nid = request.GET.get('nid',None)
if not nid:
return HttpResponse('没有任务id')
result_object = AsyncResult(id=nid, app=celery_app)
data = result_object.get()
return HttpResponse(data)
这里导入的celery_app是我们之前使用了通过__init__.py文件的celery的别名,如果之前没有写则需从celery.py下的app进行导入。
此时我们访问http://127.0.0.1:8000/test/如下:
可以看到通过获取任务id的方式获取了对象,并通过result_object.get()result_object即为执行任务的对象
拿到了我们想要的结果。
那么前面的celery任务都是立即执行的,那么我们想要使用定时任务该怎么做呢?我们只需在生成tasks任务的delay函数改为apply_async即可,该函数所包含的参数有args即传入的参数,eta值调用任务后执行任务的时间
。
所以我们修改一下test/view.py下的TestView视图函数如下:
from django.shortcuts import render, HttpResponse
from django.views import View
from tests import tasks
from celery.result import AsyncResult
from celery_test import celery_app
import datetime
# Create your views here.
class TestView(View):
def get(self, request, *args, **kwargs):
# 获取本地时间
ctime = datetime.datetime.now()
utc_ctime = datetime.datetime.utcfromtimestamp(ctime.timestamp())
target_time = utc_ctime + datetime.timedelta(seconds=10)
result = tasks.x1.apply_async(args=[2, 2], eta=target_time)
return HttpResponse(result.id)
class GetTestView(View):
def get(self, request, *args, **kwargs):
nid = request.GET.get('nid')
if not nid:
return HttpResponse('没有任务id')
result_object = AsyncResult(id=nid, app=celery_app)
data = result_object.get()
return HttpResponse(data)
此时我们访问http://127.0.0.1:8000/test/如下:
可以看到在获取到任务id的时候并没有到10秒,所以跳转到路由会等待,而当10秒已经过去了,那么celery会将任务设为执行成功,并将结果返回给了视图。
而在等待的时间我们执行的celery任务会有一个状态,而怎么获取当前任务的状态就是通过result_object.status(result_object即为执行任务的对象
)获取,而对于当前任务的状态一般是用于判断当前任务是完成,或等待中(此时的任务并没有和完成,结果是不会有值的,所以我们需要通过状态进行分支判断
)。
所以我们可以修改一下test/view.py下的GetTestView视图函数如下:
from django.shortcuts import render, HttpResponse
from django.views import View
from tests import tasks
from celery.result import AsyncResult
from celery_test import celery_app
import datetime
# Create your views here.
class TestView(View):
def get(self, request, *args, **kwargs):
# 获取本地时间
ctime = datetime.datetime.now()
utc_ctime = datetime.datetime.utcfromtimestamp(ctime.timestamp())
target_time = utc_ctime + datetime.timedelta(seconds=10)
result = tasks.x1.apply_async(args=[2, 2], eta=target_time)
return HttpResponse(result.id)
class GetTestView(View):
def get(self, request, *args, **kwargs):
nid = request.GET.get('nid')
if not nid:
return HttpResponse('没有任务id')
result_object = AsyncResult(id=nid, app=celery_app)
# 任务执行成功
if result_object.successful():
data = result_object.get()
# 将任务移除
result_object.forget()
return HttpResponse(data)
# 任务执行失败
elif result_object.failed():
return HttpResponse('任务执行失败')
# 等待或者其他情况
else:
pass
return HttpResponse('...')
此时我们访问http://127.0.0.1:8000/test/如下:
可以看到对于状态的判断我们也可以用successful、failed函数其源码也就是一行帮我们进行判断罢了,之后执行其相应的分支操作。
如果我们不需要该任务了,我们可以通过result_object.forget()把数据在backend中移除。
也可以通过result_object.revoke()进行取消,但是这只是对于没有开始执行的任务,对于开始执行的任务如果想通过revoke函数进行取消,则需要在该函数上添加一个参数(result_object.revoke(terminate=True)即可删除正在执行的任务
)。
对于celery的应用是非常的广泛的,对于秒杀、拍卖等要求都可以通过celery进行完成,并且celery也还支持周期性的定时任务操作,对于工作中是非常的方便的。