django2+django-celery-beat+celery4实现任务的动态添加等管理、多台机器部署

qq环境说明

  • win10
  • celery==4.4.2                             分布式任务队列、实现异步与定时
  • Django==2.2.5
  • django-celery-beat==2.0.0         实现定时任务的动态操作(添加/删除)等,此插件本质是对数据库表变化做                                                                             检查,一旦有数据库表改变,调度器重新读取任务进行调度
  • eventlet==0.25.2                        win10下运行worker需要这个库
  • flower==0.9.4                             可以监控任务、worker、队列的情况
  • redis==3.5.2                               此次用redis做broker和result backend

总体描述:

  • 利用celery实现分布式部署,主机器分发任务,不同机器上worker接收;
  • 至少需要两台机器进行模拟(一台机器上开不同的终端模拟多台机器也可以)

主机器A:django、django-celery-beat、celery     负责分发任务

从机器B:celery                                                    做worker机器,监听任务队列,接收主机发送的任务消息,执行相应的任务

borker:此次用redis,可用任意一台机器做broker,A或B或其他都可以

  • 结合django-celery-beat,利用django的orm模型操作sqlite3中django-celery-beat生成的表,实现任务、队列的动态设置

主机器A的目录结构:若不使用django服务,urls.py  views.py可不要

一、简单应用

(1)__init__.py

from celerytest.celery import app as celery_app

# 使得django启动时加载celery的app
__all__ = ('celery_app',)

(2)celery.py        创建celery实例

'''
通过from future import absolute_import来声明使用绝对引用,这样是为了下面的from celery import Celery引用的是系统celery模块,而不是我们自己创建的celery.py
'''
from __future__ import absolute_import, unicode_literals  
from celery import Celery
import os
import celerytest.celeryconfig
#from django.utils import timezone

# 为celery指定DJANGO_SETTINGS_MODULE环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celerytest.settings')
# 创建celery的实例app,要在指定DJANGO_SETTINGS_MODULE环境变量之后
app = Celery('tasks')
# app = Celery('tasks', broker=CELERY_BROKER_URL, backend=CELERY_RESULT_BACKEND)
app.config_from_object('celerytest.celeryconfig', namespace='CELERY')  # 命名空间 namespace='CELERY'定义所有与celery相关的配置的键名要以'CELERY_'为前缀
# app.now = timezone.now

# Load task modules from all registered Django app configs.
# 使用app.autodiscover_tasks()来自动发现django应用中的所有tasks模块
# app.autodiscover_tasks()

(3)celeryconfig.py         celery的专用配置文件  

在django的setting中,设置django的使用时区,与之对应

#from datetime import timedelta
#from kombu import Exchange, Queue
# 关闭时区
# enable_utc = False
timezone = 'Asia/Shanghai'
# DJANGO_CELERY_BEAT_TZ_AWARE = False
broker_url = 'redis://10.60.49.136:6379/1'
# 指定任务接受的序列化类型
accept_content = ['json']
result_backend = 'redis://10.60.49.136:6379/2'
# 指定任务序列化方式
task_serializer = 'json'
# 指定结果序列化的方式
result_serializer = 'json'
# 任务过期时间,celery任务执行结果的超时时间
# CELERY_TASK_RESULT_EXPIRES = 60 * 30
# 默认队列,如果一个消息不符合其他的队列就会放在默认队列里面
task_default_queue = 'default'  # 更改默认队列的名称
task_default_exchange = 'default'
task_default_routing_key = 'default'

# 异步任务
imports = ('demo.tasks',
           'demo.task2',
)

# 以下代码为写死的定时任务,如果不需要动态添加任务等操作,将以下代码释放来用即可
# # 设置详细的队列
# task_queues = {
#     Queue('default', exchange=Exchange('default'), routing_key='default'),
#     Queue('priority_high', exchange=Exchange('priority_high', type='direct'), routing_key='priority_high'),
#     Queue('priority_low', exchange=Exchange('priority_low'), routing_key='priority_low'),
# }

# # 任务进队列
# task_routes = {
#     'demo.tasks.*': {'queue': 'priority_high', 'routing_key': 'priority_high'},
#     'demo.task2.*': {'queue': 'priority_low', 'routing_key': 'priority_low'},
# }


# 启用celery的定时任务需要设置celerybeat_schedule,celery的定时任务都由celery beat来进行调度。
# CELERY_CELERYBEAT_SCHEDULE = {
#     'add-every-30-seconds': {
#          'task': 'com.fingard.tasks.ebank.control.EBankTask.start',
#          'schedule': timedelta(seconds=10),       # 每 30 秒执行一次
#          'args': ('PSB', 5, 8)                    # 任务函数参数
#     }
# }
#

(4) task.py      定义任务

如以下my_task3,在主机器上,不需要编写具体的任务内容,worker机器上才需要有具体的任务内容,如my_task2

import time
# from celery import shared_task
from celerytest.celery import app


@app.task  # 通过加上装饰器,将其注册到broker的队列中
def my_task1(x, y):
    print("任务1开始执行...")
    time.sleep(5)
    print("任务1执行结束...")
    return x + y


@app.task
def my_task2():
    print("任务2开始执行...")
    time.sleep(5)
    print("任务2执行结束...")
    return 2


@app.task
def my_task3():
    pass

(5)main.py     发送任务

from demo import tasks

result1 = tasks.my_task1.delay(2, 8)
result2 = tasks.my_task2.delay()
# my_task1.apply_async(queue=)
# print(result1.get())

二、运行一个简单应用

(1)将以上代码copy到从机B上,其中task.py中需要定义完整的任务,不能pass。打开终端,运行以下命令开启worker,

celery -A celerytest worker -l info -P eventlet

(2)主机器开启任务调度器,打开终端,输入以下命令:

释放celeryconfig.py里的定时任务代码,可执行写死的定时任务

celery beat -A celery_app -l info --pidfile=

注:--pidfile=  这个一定要加上,不然会报ERROR: Pidfile (celerybeat.pid) already exists.

(3)运行main.py文件,发送即时任务

三、动态增删改查任务、更换任务队列

(1)将django-celery-beat注册到apps中

(2)在setting.py中增加定时任务的配置

(3)由于定时器信息存储在数据库中,需要先生成对应的表,对django-celery-beat执行迁移操作,创建对应的表。

python manage.py migrate

(4)管理定时任务:

方法一:可登录网站后台admin去创建对应任务,

首先创建后台管理员账号:

python manage.py createsuperuser

登录管理后台admin:

其中Crontabs用于定时某个具体时间执行某个任务的时间,Intervals用于每隔多久执行任务的事件,具体任务的执行在Periodic tasks表中创建。

我们要创建每隔5秒执行某个任务,所以在Intervals表名后面点击Add按钮:

 然后在Periodic tasks表名后面,点击Add按钮,添加任务:

方法二:

操作django-celery-beat生成的数据表,二次开发界面,此处附上增删改查的方法

import os, django, pytz, json
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "com.fingard.celerytest.settings")
django.setup()
from django_celery_beat import models as celery_models


def task_add(task_name, task_queue, task_kwargs, task_class, task_cron):
    '''
    任务添加
    :param task_name: 任务名称
    :param task_queue: 任务队列
    :param task_kwargs: 任务传递参数
    :param task_class: 任务执行类
    :param task_cron: 任务定时的表达式
    :return:
    '''
    # 获取crontab,没有就创建
    schedule, _ = celery_models.CrontabSchedule.objects.get_or_create(**task_cron, timezone=pytz.timezone('Asia/Shanghai'))

    task = celery_models.PeriodicTask.objects.create(crontab=schedule, name=task_name, task=task_class)
    task.queue = task_queue
    task.exchange = task_queue
    task.routing_key = task_queue
    task.kwargs = json.dumps(task_kwargs)
    task.enabled = True
    task.save()
    celery_models.PeriodicTasks.changed(task)
    return task.id


def task_update(task_id, task_name, task_queue, task_cron):
    '''
    修改任务
    :param task_id: 需要修改的任务的id
    :param task_name: 修改后的任务名称,若需要修改,以字符串形式传递
    :param task_queue: 修改后的任务队列名称,若需要修改,以字符串形式传递
    :param task_cron: 修改后的时间表
    :return:
    '''
    per_task = celery_models.PeriodicTask.objects.get(id=task_id)
    if task_name and task_name != per_task.name:
        per_task.name = task_name
    if task_queue and task_queue != per_task.queue:
        per_task.queue = task_queue
        per_task.exchange = task_queue
        per_task.routing_key = task_queue
    if task_cron and task_cron != per_task.crontab:
        schedule = celery_models.CrontabSchedule.objects.create(**task_cron, timezone=pytz.timezone('Asia/Shanghai'))
        per_task.crontab = schedule
    per_task.save()
    celery_models.PeriodicTasks.changed(per_task)
    return per_task.name, per_task.queue, per_task.args, per_task.task, per_task.crontab


def task_del(task_ids):
    '''
    删除任务
    :param task_ids: 任务id的list
    :return:
    '''
    count = 0
    for task_id in task_ids:
        # 暂停执行周期性任务
        task_query = celery_models.PeriodicTask.objects.get(id=task_id)
        task_query.enabled = False
        task_query.save()
        # 删除任务
        task_query.delete()
        count += 1

    celery_models.PeriodicTasks.update_changed()
    return count


def task_list(task_name, task_queue):
    '''
    任务查询
    :param task_name: 任务名称
    :param task_queue: 任务队列
    :return: task_name任务名称、task_queue任务队列、task_args任务参数、task_class任务执行类、task_cron任务定时的表达式
    '''
    # 查询目前满足条件的所有周期性任务
    per_task = celery_models.PeriodicTask.objects.get(name=task_name, queue=task_queue)
    data = {
        "task_name": per_task.name,
        "task_queue": per_task.queue,
        "task_kwargs": per_task.kwargs,
        "task_class": per_task.task,
        "task_cron": per_task.crontab,
    }
    return data


def queue_update(queue_name_pre, queue_name_cur):
    '''
    更改任务的队列
    :param queue_name_pre: 要改的队列名称
    :param queue_name_cur: 改变后的队列名
    :return:
    '''
    all_tasks = celery_models.PeriodicTask.objects.filter(queue=queue_name_pre)
    all_tasks_ids = [per_task.id for per_task in all_tasks]
    for task_id in all_tasks_ids:
        task_query = celery_models.PeriodicTask.objects.get(id=task_id)
        task_query.queue = queue_name_cur
        task_query.exchange = queue_name_cur
        task_query.routing_key = queue_name_cur
        task_query.save()
    celery_models.PeriodicTasks.update_changed()
    all_tasks = celery_models.PeriodicTask.objects.filter(queue=queue_name_cur)
    return all_tasks

(5)启动

从机启动worker:

celery -A celerytest worker -l info -Q 监听的队列名 -P eventlet

-Q 设置worker监听的队列名,不设置的话监听默认队列 

主机器启动beat,分发任务

celery -A celerytest beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler --pidfile=

启动flower可进行监控:http://localhost:5555查看

 celery -A celerytest flower

暂时写到这里。。。。待完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章