python模块之 celery定时任务

导读:本篇文章讲解 python模块之 celery定时任务,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com


一、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也还支持周期性的定时任务操作,对于工作中是非常的方便的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/66846.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!