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

暫時寫到這裏。。。。待完善。

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