問:Celery 是什么?
答:Celery 是一個由 Python 編寫的簡單、靈活、可靠的用來處理大量信息的分布式系統,它同時提供操作和維護分布式系統所需的工具。
Celery 專注于實時任務處理,支持任務調度。(來源于網絡)
問:適用場景在哪里?
答:如圖示(來自:http://blog.csdn.net/xsj_blog/article/details/70181984)
問:生產者和消費者模式定義是什么?
答:
(1)生產者->負責產生數據;
(2)消費者->負責數據處理;
(3)緩沖區->解耦生產者和消費者,減少依賴,主要是通過消息隊列來進行兩點之間的通訊處理。
圖示:
問:什么是任務隊列?
答:任務隊列是一種在線程或機器間分發任務的機制。
問:什么是消息隊列?
答:消息隊列的輸入是工作的一個單元,稱為任務,獨立的職程(Worker)進程持續監視隊列中是否有需要處理的新任務。
問:職程有什么作用?
答:Celery 用消息通信,通常使用中間人(Broker)在客戶端和職程間斡旋。這個過程從客戶端向隊列添加消息開始,之后中間人把消息派送給職程,職程對消息進行處理。如下圖所示:
問:Celery的架構三部分是哪幾個部分?
答:Celery的架構由三部分組成,消息中間件(message broker),任務執行單元(worker)和任務執行結果存儲(task result store)組成。
(1)消息中間件
PS: Celery本身不提供消息服務,但是可以方便的和第三方提供的消息中間件集成,包括,RabbitMQ,Redis,MongoDB等。
(2)任務執行單元
PS: Worker是Celery提供的任務執行的單元,worker并發的運行在分布式的系統節點中。
(3)任務結果存儲
PS: Task result store用來存儲Worker執行的任務的結果,Celery支持以不同方式存儲任務的結果,包括Redis,MongoDB,Django ORM,AMQP等。
(任務調度)Celery Beat:任務調度
Celery Beat:任務調度器,Beat 進程會讀取配置文件的內容,周期性地將配置中到期需要執行的任務發送給任務隊列。
消息分發與任務調度的實現機制(來自:http://blog.csdn.net/xsj_blog/article/details/70181984)
1:—>producer發出調用請求(message包含所調用任務的相關信息)
2:—>celery服務啟動時,會產生一個或多個交換機(exchanges),對應的交換機 接收請求message
3:—>交換機根據message內容,將message分發到一個或多個符合條件的隊列(queue)
4:—>每個隊列上都有一個或多個worker在監聽,在監聽到符合條件的message到達后,worker負責進行任務處理,任務處理完被確認后,隊列中的message將被刪除。
注釋:Exchange和Queue都是Rabbitmq中的概念
Exchange:交換機,決定了消息路由規則;
Queue:消息隊列;
Channel:進行消息讀寫的通道;
Bind:綁定了Queue和Exchange,意即為符合什么樣路由規則的消息,將會放置入哪一個[消息隊列];
調圖流程圖示:(來自https://www.cnblogs.com/forward-wang/p/5970806.html)
實踐步驟:
相關依賴:
第1步:首先搭建bottle客戶端端,進行任務委派:
main.py
#!/usr/bin/evn python
# coding=utf-8
"""
Author = zyx
@Create_Time: 2018/1/30 15:58
@version: v1.0.0
@File: main.py
@文件功能描述:
"""
from bottle import route, run
@route('/')
def index():
return '訪問了首頁!'
run(host='127.0.0.1', port=8080, debug=True, reloader=True)
啟動wen服務應用訪問:
第2步:編寫對應Celery任務模塊celery_test
第3步:編寫對應Celery任務模塊啟動配置文件
setting.py配置(使用配置的方式來啟動相關worker來處理 任務):
# coding:utf-8
from datetime import timedelta
from kombu import Exchange, Queue
# 配置消息中間件Broker
BROKER_URL = 'redis://127.0.0.1:6379/0'
# 配置結果存貯Backend
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'
# 指定時區,默認是 UTC
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
# # 不需要返回任務狀態,即設置以下參數為True
# 如果不需要某個任務的結果,應該確保Celery不去獲取這些結果。這是通過裝飾器@task(ignore_result=True)來做的。如果所有的任務結果都忽略了,就不必定義結果后臺。這可以讓性能大幅提高。
CELERY_IGNORE_RESULT = True
# 任務序列化和反序列化使用msgpack方案
CELERY_TASK_SERIALIZER = 'json'
# 讀取任務結果一般性能要求不高,所以使用了可讀性更好的JSON
CELERY_RESULT_SERIALIZER = 'json'
# CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任務過期時間,不建議直接寫86400,應該讓這樣的magic數字表述更明顯
# celery worker的并發數 也是命令行-c指定的數目,事實上實踐發現并不是worker也多越好,保證任務不堆積,加上一定新增任務的預留就可以
CELERYD_CONCURRENCY = 10
# celery worker 每次去rabbitmq取任務的數量,我這里預取了4個慢慢執行,因為任務有長有短沒有預取太多
CELERYD_PREFETCH_MULTIPLIER = 4
CELERY_ACCEPT_CONTENT = ['json'] # 指定接受的內容類型
# 默認的隊列,如果一個消息不符合其他的隊列就會放在默認隊列里面
CELERY_DEFAULT_QUEUE = "default"
CELERY_QUEUES = (
Queue('default', Exchange('default'), routing_key='default'),
Queue('for_add', Exchange('for_task_add'), routing_key='for_task_add'),
Queue('for_send_email', Exchange('for_task_email'), routing_key='for_task_email'),
)
CELERY_ROUTES = {
'celery_test.tasks.add': {'queue': 'for_add', 'routing_key': 'for_task_add'},
'celery_test.tasks.send_mail': {'queue': 'for_send_email', 'routing_key': 'for_task_email'},
}
CELERYBEAT_SCHEDULE = {
'send_mail': {
'task': 'celery_test.tasks.send_mail',
'schedule': timedelta(seconds=30),
},
'add': {
'task': 'celery_test.tasks.add',
'schedule': timedelta(seconds=10),
'args': (16, 16)
}
}
PS:以下代碼就解釋:
CELERY_QUEUES = (
Queue('default', Exchange('default'), routing_key='default'),# 這是上面指定的默認隊列
Queue('for_add', Exchange('for_task_add'), routing_key='for_task_add'), # 這是一個for_add隊列 凡是for_task_add開頭的routing key都會被放到這個隊列
Queue('for_send_email', Exchange('for_task_email'), routing_key='for_task_email'),
# 這是一個or_send_email'隊列 凡是for_task_email開頭的routing key都會被放到這個隊列
)
第4步:編寫對應Celery實例
server.py
from celery import Celery
app=Celery('celery_test',include=['celery_test.tasks'])
app.config_from_object('celery_test.setting')
if __name__=='__main__':
app.start()
第5步:編寫對應任務
tasks.py
# coding:utf-8
from celery_test.server import app
@app.task(bind=True)
def add(self,x, y):
return x + y
@app.task(bind=True)
def send_mail(self,x, y):
return x - y
第6步:修改main.py進行任務調用
main.py
from bottle import route, run, redirect
from celery_test import tasks
# @route('/add')
# def index():
# tasks.add.daley(888, 45)
# return '訪問了add!'
@route('/send_mail')
def index():
task = tasks.send_mail.delay(888, 45)
print('訪問了send_mail!')
return redirect('/tasks_status/' + task.id) # 重定向到首頁(可以 )
@route('/tasks_status/<task_id>')
def index(task_id):
# 獲取異步任務結果
task = tasks.send_mail.AsyncResult(task_id)
# 等待處理
if task.state == 'PENDING':
response = {'state': task.state, 'current': 0, 'total': 1}
print('PENDING:', response)
elif task.state != 'FAILURE':
response = {'state': task.state, 'current': task.info.get('current', 0), 'total': task.info.get('total', 1)}
# 處理完成
if 'result' in task.info:
response['result'] = task.info['result']
print('處理完成:', response)
else:
# 后臺任務出錯
response = {'state': task.state, 'current': 1, 'total': 1}
print('后臺任務出錯:', response)
run(host='127.0.0.1', port=8080, debug=True, reloader=True)
第7步:啟動指定的隊列
celery -A celery_test.server worker -l info -Q for_send_email
啟動成功如圖示:
第8步:啟動web服務調用對應的URL請求異步處理異步任務
調用:http://127.0.0.1:8080/send_mail
image.png
即時查看任務處理情況:
http://127.0.0.1:8080/tasks_status/0079d834-d918-4ad7-88dd-f23c5eeb09dc
查看對應的celery的運行 情況:
image.png
問:監控Celery任務執行情況?
答:Flower是基于web的監控和管理Celery的工具.
相關文檔:
http://flower-docs-cn.readthedocs.io/zh/latest/
安裝pip install flower
啟動flower(flower默認的端口是5555.)
celery flower --port=5555 --broker=redis://localhost:6379/0
celery flower --broker=amqp://guest:guest@192.168.xx.xxx:5672//
啟動任務查看
celery flower --port=5555 --broker=redis://localhost:6379/0
訪問:127.0.0.1:5555
進行任務執行:http://127.0.0.1:8080/send_mail
查看任務執行結果
PS其他命令
============================================================================
前臺啟動
啟動指定的隊列
celery -A celery_test.server worker -l info -Q for_send_email
celery -A celery_test.server worker -l info -Q for_add
啟動定時相關的任務隊列
celery -A celery_test.server beat
celery -A celery_test.server worker -l info -Q for_send_email
celery -A celery_test.server worker -l info -Q for_add
============================================================================
后臺啟動
celery multi start w1 -A proj -l info
celery multi restart w1 -A proj -l info
# 異步關閉 立即返回
celery multi stop w1 -A proj -l info
# 等待關閉操作完成
celery multi stopwait w1 -A proj -l info
調用任務:
add.apply_async((2, 2), queue='lopri', countdown=10)
# 指定要發送到哪個隊列 運行時間延遲countdown