celery任務隊列

celery分佈式任務隊列

一. celery 簡介

Celery 是一個專注於實時處理和任務調度的分佈式任務隊列, 同時提供操作和維護分佈式系統所需的工具… 所謂任務就是消息, 消息中的有效載荷中包含要執行任務需要的全部數據.

Celery 是一個分佈式隊列的管理工具, 可以用 Celery 提供的接口快速實現並管理一個分佈式的任務隊列.

Celery 本身不是任務隊列, 是管理分佈式任務隊列的工具. 它封裝了操作常見任務隊列的各種操作, 我們使用它可以快速進行任務隊列的使用與管理.

Celery 特性 :

方便查看定時任務的執行情況, 如 是否成功, 當前狀態, 執行任務花費的時間等.

使用功能齊備的管理後臺或命令行添加,更新,刪除任務.

方便把任務和配置管理相關聯.

可選 多進程, Eventlet 和 Gevent 三種模型併發執行.

提供錯誤處理機制.

提供多種任務原語, 方便實現任務分組,拆分,和調用鏈.

支持多種消息代理和存儲後端.

Celery 是語言無關的.它提供了python 等常見語言的接口支持.

二. celery 組件

1. Celery 扮演生產者和消費者的角色,

Celery Beat : 任務調度器. Beat 進程會讀取配置文件的內容, 週期性的將配置中到期需要執行的任務發送給任務隊列.

Celery Worker : 執行任務的消費者, 通常會在多臺服務器運行多個消費者, 提高運行效率.

Broker : 消息代理, 隊列本身. 也稱爲消息中間件. 接受任務生產者發送過來的任務消息, 存進隊列再按序分發給任務消費方(通常是消息隊列或者數據庫).

Producer : 任務生產者. 調用 Celery API , 函數或者裝飾器, 而產生任務並交給任務隊列處理的都是任務生產者.

Result Backend : 任務處理完成之後保存狀態信息和結果, 以供查詢.

Celery架構圖

img

2. 產生任務的方式 :

1.發佈者發佈任務(WEB 應用)

2.任務調度按期發佈任務(定時任務)

3. celery 依賴三個庫: 這三個庫, 都由 Celery 的開發者開發和維護.

​ billiard : 基於 Python2.7 的 multisuprocessing 而改進的庫, 主要用來提高性能和穩定性.

​ librabbitmp : C 語言實現的 Python 客戶端,

​ kombu : Celery 自帶的用來收發消息的庫, 提供了符合 Python 語言習慣的, 使用 AMQP 協議的高級藉口.

三. 選擇消息代理

​ 使用於生產環境的消息代理有 RabbitMQ 和 Redis, 官方推薦 RabbitMQ.

四. Celery 序列化

​ 在客戶端和消費者之間傳輸數據需要 序列化和反序列化. Celery 支出的序列化方案如下所示:

img

五. 安裝,配置與簡單示例

Celery 配置參數彙總

img

代碼示例 :

# 安裝$ pip install celery, redis, msgpack

# 配置文件 celeryconfig.py

CELERY_BROKER_URL = ‘redis://localhost:6379/1’

CELERY_RESULT_BACKEND = ‘redis://localhost:6379/0’

CELERY_TASK_SERIALIZER = ‘json’

CELERY_RESULT_SERIALIZER = ‘json’

CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24

# 任務過期時間 CELERY_ACCEPT_CONTENT = [“json”]

# 指定任務接受的內容類型.

# 初始化文件 celery.py

from future import absolute_import

from celery import Celery

app = Celery(‘proj’, include=[“proj.tasks”])

app.config_from_object(“proj.celeryconfig”)

if name == “main”:

​ app.start()

# 任務文件 tasks.py

from future import absolute_import

from proj.celery import app

@app.task def add(x, y):

​ return x + y # 啓動消費者

$ celery -A proj worker -l info

# 在終端中測試 > from proj.tasks import add

> r = add.delay(2,4)

> r.result 6

> r.status u"SUCCESS"

> r.successful() True

> r.ready()

# 返回布爾值, 任務執行完成, 返回 True, 否則返回 False. > r.wait()

# 等待任務完成, 返回任務執行結果. > r.get()

# 獲取任務執行結果 > r.result

# 任務執行結果. > r.state

# PENDING, START, SUCCESS > r.status

# PENDING, START, SUCCESS

# 使用 AsyncResult 方式獲取執行結果.

# AsyncResult 主要用來存儲任務執行信息與執行結果(類似 js 中的 Promise 對象), > from celery.result import AsyncResult > AsyncResult(task_id).get() 4

說明:以上代碼爲原博客中內容,實測的話結合flask,redis 存在版本問題。後續博客處理

六. 調用任務的方法 :

1. delay

task.delay(args1, args2, kwargs=value_1, kwargs2=value_2)

2. apply_async

delay 實際上是 apply_async 的別名, 還可以使用如下方法調用, 但是 apply_async 支持更多的參數:

task.apply_async(args=[arg1, arg2], kwargs={key:value, key:value})

支持的參數 :

countdown : 等待一段時間再執行.

add.apply_async((2,3), countdown=5)

eta : 定義任務的開始時間.

add.apply_async((2,3), eta=now+tiedelta(second=10))

expires : 設置超時時間.

add.apply_async((2,3), expires=60)

retry : 定時如果任務失敗後, 是否重試.

add.apply_async((2,3), retry=False)

retry_policy : 重試策略.

​ max_retries : 最大重試次數, 默認爲 3 次.

​ interval_start : 重試等待的時間間隔秒數, 默認爲 0 , 表示直接重試不等待.

​ interval_step : 每次重試讓重試間隔增加的秒數, 可以是數字或浮點數, 默認爲 0.2

​ interval_max : 重試間隔最大的秒數, 即 通過 interval_step 增大到多少秒之後, 就不在增加了, 可以是數字或者浮點數, 默認爲 0.2 .

自定義發佈者,交換機,路由鍵, 隊列, 優先級,序列方案和壓縮方法:

task.apply_async((2,2), compression=‘zlib’, serialize=‘json’, queue=‘priority.high’, routing_key=‘web.add’, priority=0, exchange=‘web_exchange’)

七. 指定隊列 :

Celery 默認使用名爲 celery 的隊列 (可以通過 CELERY_DEFAULT_QUEUE 修改) 來存放任務. 我們可以使用 優先級不同的隊列 來確保高優先級的任務優先執行.

# 定義任務隊列.

Queue(‘default’, routing_key=“task.#”),

# 路由鍵 以 “task.” 開頭的消息都進入 default 隊列.

Queue(‘web_tasks’, routing_key=“web.#”)

# 路由鍵 以 “web.” 開頭的消息都進入 web_tasks 隊列.)

CELERY_DEFAULT_EXCHANGE = ‘tasks’

# 默認的交換機名字爲

tasksCELERY_DEFAULT_EXCHANGE_KEY = ‘topic’

# 默認的交換機類型爲

topicCELERY_DEFAULT_ROUTING_KEY = ‘task.default’

# 默認的路由鍵是 task.default , 這個路由鍵符合上面的 default 隊列.

CELERY_ROUTES = { ‘proj.tasks.add’: { ‘queue’: ‘web_tasks’, ‘routing_key’: ‘web.add’, }}

# 使用指定隊列的方式啓動消費者進程.$ celery -A proj worker -Q web_tasks -l info

# 該 worker 只會執行 web_tasks 中任務, 我們可以合理安排消費者數量, 讓 web_tasks 中任務的優先級更高.

這段沒試過

閱後即焚模式(transient):

from kombu import QueueQueue(‘transient’, routing_key=‘transient’, delivery_mode=1)

八. 使用任務調度

使用 Beat 進程自動生成任務.

# 修改配置文件,

# 下面的任務指定 tasks.add 任務 每 10s 跑一次, 任務參數爲 (16,16).

from datetime import timedelta

CELERYBEAT_SCHEDULE = { ‘add’: {

​ ‘task’: ‘proj.tasks.add’,

​ ‘schedule’: timedelta(seconds=10),

​ ‘args’: (16, 16) }}

# crontab 風格

from celery.schedules import crontab

CELERYBEAT_SCHEDULE = { “add”: {

​ “task”: “tasks.add”,

​ “schedule”: crontab(hour="*/3", minute=12),

​ “args”: (16, 16), } }

# 啓動 Beat 程序$ celery beat -A proj

# 之後啓動 worker 進程.$ celery -A proj worker -l info 或者$ celery -B -A proj worker -l info


使用自定義調度類還可以實現動態添加任務. 使用 Django 可以通過 Django-celery 實現在管理後臺創建,刪除,更新任務, 是因爲他使用了自定義的 調度類 djcelery.schedulers.DatabaseScheduler .

九. 任務綁定, 記錄日誌, 重試

# 修改 tasks.py 文件.

from celery.utils.log import get_task_loggerlogger = get_task_logger(name)

@app.task(bind=True)def div(self, x, y):

​ logger.info(('Executing task id {0.id},

​ args: {0.args!r}’ ’

​ kwargs: {0.kwargs!r}’).format(self.request))

​ try:

​ result = x/y

​ except ZeroDivisionError as e:

​ raise self.retry(exc=e, countdown=5, max_retries=3)

# 發生 ZeroDivisionError 錯誤時, 每 5s 重試一次, 最多重試 3 次.

return result


當使用 bind=True 參數之後, 函數的參數發生變化, 多出了參數 self, 這這相當於把 div 編程了一個已綁定的方法, 通過 self 可以獲得任務的上下文.

日誌輸出目前未處理,實際問題需要後面處理,由於是與flask整合。所以需要看怎麼管理日誌

十. 信號系統 :

信號可以幫助我們瞭解任務執行情況, 分析任務運行的瓶頸. Celery 支持 7 種信號類型.

1.任務信號

​ before_task_publish : 任務發佈前

​ after_task_publish : 任務發佈後

​ task_prerun : 任務執行前

​ task_postrun : 任務執行後

​ task_retry : 任務重試時

​ task_success : 任務成功時

​ task_failure : 任務失敗時

​ task_revoked : 任務被撤銷或終止時

2.應用信號

3.Worker 信號

4.Beat 信號

5.Eventlet 信號

6.日誌信號

7.命令信號

代碼示例 :

# 在執行任務 add 之後, 打印一些信息.

@after_task_publish

def task_send_handler(sender=None, body=None, **kwargs):

​ print 'after_task_publish: task_id: {body[id]};

​ sender: {sender}’.format(body=body, sender=sender)

十一. 子任務與工作流:(這塊比較重要)

可以把任務 通過簽名的方法傳給其他任務, 成爲一個子任務.

from celery import signaturetask = signature(‘task.add’, args=(2,2), countdown=10) tasktask.add(2,2)

# 通過簽名生成任務task.apply_async()

還可以通過如下方式生成子任務 :

from proj.task import addtask = add.subtask((2,2), countdown=10)# 快捷方式 add.s((2,2), countdown-10) task.apply_async()

自任務實現片函數的方式非常有用, 這種方式可以讓任務在傳遞過程中財傳入參數.

partial = add.s(2)partial.apply_async((4,))

子任務支持如下 5 種原語,實現工作流. 原語表示由若干指令組成的, 用於完成一定功能的過程

1.chain : 調用連, 前面的執行結果, 作爲參數傳給後面的任務, 直到全部完成, 類似管道.

from celery import chainres = chain(add.s(2,2), add.s(4), add.s(8))()res.get() 管道式: (add.s(2,2) | add.s(4) | add.s(8))().get()


2.group : 一次創建多個(一組)任務.

from celery import group res = group(add.s(i,i)foriinrange(10))()res.get()

3.chord : 等待任務全部完成時添加一個回調任務.

res = chord((add.s(i,i)foriinrange(10)), add.s([‘a’]))()res.get()# 執行完前面的循環, 把結果拼成一個列表之後, 再對這個列表 添加 ‘a’.[0,2,4,6,8,10,12,14,16,18,u’a’]

4.map/starmap : 每個參數都作爲任務的參數執行一遍, map 的參數只有一個, starmap 支持多個參數.

add.starmap(zip(range(10), range(10))) 相當於: @app.taskdef temp():return[add(i,i)foriinrange(10)]

5.chunks : 將任務分塊.

res = add.chunks(zip(range(50), range(50)),10)()res.get()

在生成任務的時候, 應該充分利用 group/chain/chunks 這些原語.

十二. 其他

關閉不想要的功能 :

@app.task(ignore_result=True) # 關閉任務執行結果.def func(): pass CELERY_DISABLE_RATE_LIMITS=True # 關閉限速.

根據任務狀態執行不同操作 :

# tasks.py

class MyTask(Task):

​ def on_success(self, retval, task_id, args, kwargs):

​ print ‘task done: {0}’.format(retval)

​ return super(MyTask, self).on_success(retval, task_id, args, kwargs)

​ def on_failure(self, exc, task_id, args, kwargs, einfo):

​ print ‘task fail, reason: {0}’.format(exc)

​ return super(MyTask, self).on_failure(exc, task_id, args, kwargs, einfo)

# 正確函數, 執行

MyTask.on_success() :

@app.task(base=MyTask)

​ def add(x, y):

​ return x + y # 錯誤函數, 執行 MyTask.on_failure() :

@app.task #普通函數裝飾爲

celery taskdef add(x, y):

​ raise KeyError return x + y

十三. Celery 管理命令

任務狀態回調 :

img

普通啓動命令 :

$ celery -A proj worker -l info

十四. 在 Flask 中使用 Celery

Flask 文檔: 基於 Celery 的後臺任務

在 Flask 中使用 Celery

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