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
暫時寫到這裏。。。。待完善。