ベテランのプログラマーはセロリのメモをすぐに覚えます
目的
景気低迷の状況においては、テクノロジースタックを拡大し、競争力を向上させることしかできません。
構造図
1. ブローカー: 通常、タスク キューを保存する Redis または MQ
2. ワーカー: コンシューマー
3. バックエンド: 結果ストレージ キュー (これも Redis または MQ)
4. タスク: 個人的には、プロデューサーです。
インストール
pip install celery
私は個人的に 5.3.1 を直接使用していますが、使い始めるとすぐに最新バージョンになるため、他の人の以前のチュートリアルを読むときに問題が発生するという落とし穴があります。
最初のピット
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\billiard\pool.py", line 362, in workloop
result = (True, prepare_result(fun(*args, **kwargs)))
File "C:\Python39\lib\site-packages\celery\app\trace.py", line 635, in fast_trace_task
tasks, accept, hostname = _loc
ValueError: not enough values to unpack (expected 3, got 0)
このエラーは、your_func.lay() タスクを通じて celery が実行されると報告されます。GPT がバイパスされることについて質問しましたが、後でインターネットで解決策を見つけたので、以下に宿題をコピーします。
解決する
- Windows 10 システム.
他の方の説明によると、win10 で celery4.x を実行するとこの問題が発生するとのことですが、解決方法は以下の通りです. 原理は不明ですが、まずイベントレットをインストールします。
pip install eventlet
次に、ワーカーの起動時に次のようにパラメーターを追加します。
celery -A celery_tasks.main worker -l info -P eventlet
そうすれば普通に呼び出せます
基本的な使い方
my_task.py という名前の Python ファイルを作成します。
import celery
import time
backend='redis://127.0.0.1:6379/1'
broker='redis://127.0.0.1:6379/2'
cel=celery.Celery('first_task',backend=backend,broker=broker)
@cel.task
def send_email(name):
print("向%s发送邮件..."%name)
time.sleep(5)
print("向%s发送邮件完成"%name)
return "ok"
@cel.task
def send_msg(name):
print("向%s发送短信..."%name)
time.sleep(5)
print("向%s发送短信完成"%name)
return "ok"
上記の my_task、cel、および frist_task の名前を見て、後で出力されるデバッグ情報を実行するときにどのパラメーターがどのパラメーターであるかを比較できるように、名前を区別してください。
走る
D:\代码\my_code\study\study_celery>celery -A my_task worker -l info -P eventlet
-------------- celery@WINDOWS-3T5S0CL v5.1.2 (sun-harmonics)
--- ***** -----
-- ******* ---- Windows-10-10.0.10586-SP0 2023-07-02 20:24:05
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: first_task:0x2b9d54582e0
- ** ---------- .> transport: redis://:**@120.79.235.7:6379/63
- ** ---------- .> results: redis://:**@120.79.235.7:6379/63
- *** --- * --- .> concurrency: 8 (eventlet)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. my_task.send_email
. my_task.send_msg
[2023-07-02 20:24:06,312: INFO/MainProcess] Connected to redis://:**@120.79.235.7:6379/63
[2023-07-02 20:24:06,394: INFO/MainProcess] mingle: searching for neighbors
[2023-07-02 20:24:08,250: INFO/MainProcess] mingle: sync with 1 nodes
[2023-07-02 20:24:08,254: INFO/MainProcess] mingle: sync complete
[2023-07-02 20:24:08,368: INFO/MainProcess] pidbox: Connected to redis://:**@120.79.235.7:6379/63.
[2023-07-02 20:24:08,500: INFO/MainProcess] celery@WINDOWS-3T5S0CL ready.
上記では、それぞれの名前が表す内容を区別しやすくするために、あまりにも多くの異なる名前を作成するよう求められています。
-
celery -A my_task worker -l info -P
イベントレット起動コマンドの my_task はファイル名です。起動コマンドに .py を含めないでください -
app: first_task:0x2b9d54582e0
アプリの名前は、celery.Celery がアプリを生成するときに渡される最初のパラメーターです。 -
Transport: redis://: @120.79.235.7:6379/63
results: redis://: @120.79.235.7:6379/63
中間 Redis と結果 Redis、さらに多くのプロジェクト、テスト、正式な環境がある場合は、覚えておいてください。開始時にさらに確認する -
[tasks]
. my_task.send_email
. my_task.send_msg
ここでは、セロリアプリにどのタスクがあるかを明確に確認できます。 -
celery@WINDOWS-3T5S0CLready.
このメッセージが表示されると、redis は正常に実行されています。
起動情報を侮らず、起動するたびに中身の情報を確認してください。
セロリアプリを実行してみましょう
まず、ファイル「Produce_task.py」を作成します。
from my_task import send_email, send_msg
email_result = send_email.delay("hello")
print(email_result.id)
msg_result = send_msg.delay("world")
print(msg_result.id)
上記のコードを実行すると、send_msg と send_email という 2 つの実行コマンドが実行中の celery に送信されることになります。遅延で直接渡されるパラメータは send_msg のパラメータです。
罠を踏む
ここにもう 1 つの落とし穴があります。ブローカーとバックエンドが同じ Redis 内にある場合、2 つの遅延が発生します。1 つだけが実行されるため、問題は見つかりません。その後、それを 2 つの遅延に置き換えるだけです。問題の根本原因は後で見つけるので、今は進捗状況を学ぶつもりはありません。
実行結果の表示
すべての実行結果はバックエンドのredis上で確認でき、成功・失敗は文字列型で保存されます。DBルートディレクトリに直接保存
実行結果を取得する
# 成功结果
async_succ_result = AsyncResult(id="23c7ebfd-5bf6-4f83-b7c8-9c870b168d17", app=cel)
# 失败结果
async_fail_result = AsyncResult(id="923360d1-caa4-41ca-9887-3bc404ff0803", app=cel)
# 判断是否成功
success_flag1 = async_succ_result.successful()
success_flag2 = async_fail_result.successful()
# 判断是否失败
failure_flag1 = async_succ_result.failed()
failure_flag2 = async_fail_result.failed()
# 获取结果,如果是成功,则返回之前函数里的return值
result = async_succ_result.get()
# 获取结果,如果是失败,则会报异常
try:
result = async_fail_result.get()
except Exception as e:
print(e)
稼働状況
# 运行结果状态
if async_succ_result.status == 'PENDING':
print('任务等待中被执行')
elif async_succ_result.status == 'RETRY':
print('任务异常后正在重试')
elif async_succ_result.status == 'STARTED':
print('任务已经开始被执行')
elif async_succ_result.status == 'FAILURE':
print('任务执行失败')
elif async_succ_result.status == 'SUCCESS':
print('任务执行成功')
関数内のコメントにはこれらの 5 つの状態のみが含まれます
結果の削除
async_succ_result.forget()
結果が削除されると、取得が停止することに注意してください。
マルチタスク構造
- 大量のタスクを実行する必要がある場合、タスクを構造的に階層化する必要があり、その際、メンテナンスのために複数のファイルを区別する必要があります。
マルチタスクのルート ディレクトリとして新しいディレクトリ my_multi_tasks を作成します。
- celery.py ファイルを作成する
import celery
backend='redis://127.0.0.1:6379/1'
broker='redis://127.0.0.1:6379/2'
second_cel = celery.Celery(
'second_tasks',
backend=backend,
broker=broker,
include={
'my_multi_tasks.my_task_01',
'my_multi_tasks.my_task_02'
}
)
ここでのファイル名は celery である必要があります。celery.exe コントローラーがコマンドを実行すると、プロジェクトの下に celery ファイルが検出され、マルチタスク構造が構成されます。名前がセロリではない場合、次のようなエラーが報告されます。
D:\代码\my_code\study\study_celery>celery -A my_multi_tasks worker -l info -P eventlet
Usage: celery [OPTIONS] COMMAND [ARGS]...
Error: Invalid value for '-A' / '--app':
Unable to load celery application.
Module 'my_multi_tasks' has no attribute 'celery'
モジュール 'my_multi_tasks' には属性 'celery' がありません。これは、my_multi_tasks に celery 属性がないことを示しています。デフォルトのプログラムは .celery であることがわかります。
- my_task_01.py ファイルを作成する
import time
from my_multi_tasks.elery import second_cel
@second_cel.task
def second_send_email(name):
print(f"向{name}发送邮件".center(30, '1'))
time.sleep(5)
print(f"向{name}发送邮件完成".center(30, '1'))
return "ok"
- my_task_02.py ファイルを作成する
import time
from my_multi_tasks.elery import second_cel
@second_cel.task
def second_send_msg(name):
print(f"向{name}发送短信".center(30, '2'))
time.sleep(5)
print(f"向{name}发送短信完成".center(30, '2'))
return "ok"
send_email と send_msg をそれぞれ task01 と task02 に配置します。関数の前にマルチタスク構造のセロリを忘れずに追加してください。
- プロデュース_タスク_秒.py ファイルを作成します
from my_multi_tasks.my_task_01 import second_send_email
from my_multi_tasks.my_task_02 import second_send_msg
email_result = second_send_email.delay('second_hello')
msg_result = second_send_msg.delay('second_world')
- コマンド celery -A my_multi_tasks worker -l info -Peventlet を実行して
celery を起動した後、Produce_task_second.py ファイルを使用してタスクを送信します。タスクの実行プロセスを表示できます
スケジュールされたタスク
タスク構造は依然として、上記のマルチタスク構造を直接コピーしたタスク本体です。スケジュールされたタスクの p にもう 1 つのプロデュース ファイルを追加する場合の説明
from datetime import datetime
from my_multi_tasks.my_task_01 import second_send_email
from my_multi_tasks.my_task_02 import second_send_msg
# 指定时间发送
time_temp = datetime(2023, 7, 4, 6, 19, 00)
time_temp_utc = datetime.utcfromtimestamp(time_temp.timestamp())
result = second_send_email.apply_async(args=['kola', ], eta=time_temp_utc)
print(result.id)
# 延迟发送
from datetime import timedelta
time_now = datetime.now()
time_now_utc = datetime.utcfromtimestamp(time_now.timestamp())
time_now_utc_new = time_now_utc + timedelta(seconds=20)
result2 = second_send_msg.apply_async(args=['cat', ], eta=time_now_utc_new)
print(result2.id)
- 注:
1. Celery のスケジュールされたタスクはデフォルトで国際標準 UTC に基づいているため、中国時間には UTC 変換が必要です
2. 上記のステートメントを実行すると、スクリプトが終了し、タスクがキューにスローされます。待ち時間経過後に実行
3. 引数に渡されるパラメータに注意してください ['kola', ] ですカンマが入っているのでタプルが渡されます。
構成情報とビートを通じてスケジュールされたタスクを開始します
ビート社はセロリの非常に重要な生産者です。ワーカーと同様にスクリプトサービスであり、起動後に実行されているスクリプトプログラムですので、設定を行ってください。
import celery
from celery.schedules import crontab
backend='redis://127.0.0.1:6379/1'
broker='redis://127.0.0.1:6379/2'
second_cel = celery.Celery(
'second_tasks',
backend=backend,
broker=broker,
include={
'my_multi_tasks.my_task_01',
'my_multi_tasks.my_task_02'
}
)
# 设置定时任务的执行时间的时区,改为中国,关闭utc
second_cel.conf.timezone = 'Asia/Shanghai'
second_cel.conf.enable_utc = False
# 发送任务的调度器
second_cel.conf.beat_schedule = {
# 随意命名
'add-task-every-1-minute': {
# 传参
'args': ('张三',),
# 执行函数的路径,指定second_send_email函数
'task': 'my_multi_tasks.my_task_01.second_send_email',
# 调度时间
# 'schedule': 10.0, # 每几秒,执行一次
# 'schedule': timedelta(seconds=6), # 每几秒,执行一次,但timedelta更丰富,比如day,hour
'schedule': crontab(minute="*/1"), # 每一分钟,执行一次
},
'add-task-birthday': {
'args': ('李四',),
'task': 'my_multi_tasks.my_task_02.second_send_msg',
# 每年4月11号,8点42分执行
'schedule': crontab(minute=10, hour=5, day_of_month=5, month_of_year=7),
},
}
Celery は、タスクを実行するためのデフォルトのタイムゾーンの直接変更をサポートしています: Second_cel.conf.timezone は、
設定を通じてスケジュールされたタスクのリストを生成します: Second_cel.conf.beat_schedule
- 起動コマンド: celery -A my_multi_tasks beat -l info -P は
コンシューマのエグゼキュータを指定するため、ここで -P イベントレットを追加する必要がないことに注意してください。このプロデューサーだから、必要ない。
実行して観察すると、生産者ビートと消費者ワーカーが完全に分離されていることがわかります。Beat もプログラムであり、コマンド ラインから起動されると、設定テーブルに設定された時間に従って、スケジュールされたタスクをミドルウェア ブローカーのキー セロリにスローするだけです。以下は例です
{
"body": "W1siXHU2NzRlXHU1NmRiIl0sIHt9LCB7ImNhbGxiYWNrcyI6IG51bGwsICJlcnJiYWNrcyI6IG51bGwsICJjaGFpbiI6IG51bGwsICJjaG9yZCI6IG51bGx9XQ==",
"content-encoding": "utf-8",
"content-type": "application/json",
"headers": {
"lang": "py",
"task": "my_multi_tasks.my_task_02.second_send_msg",
"id": "1dce9df1-da1e-4c4e-ac6b-be170b815400",
"shadow": null,
"eta": null,
"expires": null,
"group": null,
"group_index": null,
"retries": 0,
"timelimit": [null, null],
"root_id": "1dce9df1-da1e-4c4e-ac6b-be170b815400",
"parent_id": null,
"argsrepr": "['\u674e\u56db']",
"kwargsrepr": "{}",
"origin": "gen12600@WINDOWS-3T5S0CL",
"ignore_result": false
},
"properties": {
"correlation_id": "1dce9df1-da1e-4c4e-ac6b-be170b815400",
"reply_to": "3f3250fb-6ed4-3d13-83db-a7d8e30c8ee9",
"delivery_mode": 2,
"delivery_info": {
"exchange": "",
"routing_key": "celery"
},
"priority": 0,
"body_encoding": "base64",
"delivery_tag": "88d2a3dd-5612-4ef7-ae36-0d2484b9a468"
}
}
観察する意味のあるパラメーター値は 2 つだけであるように感じられます。1 つはタスク関数、もう 1 つはパラメーター
"task": "my_multi_tasks.my_task_02.second_send_msg"、
"argsrepr": "['\u674e \u56db']"、
- ビート開始後、時間内にタスクを消費しないと、どんどんタスクが溜まってしまうので注意してください。ワーカーが開始すると、これらすべてのタスクが実行されます。いくつかの期限切れ非実行メカニズムが必要になる場合があります
Django の Celery アプリケーション
新しい Django プロジェクトを作成し、詳細設定のアプリケーション名に app01 という名前を付けると、プロジェクトの作成時にアプリが作成されます。
app01 のビューを指す URL にテスト パスを作成し
、app01 のビュービュー関数にテスト関数を追加します。Web サイトがページ 127.0.0.1/test にアクセスすることをテストし、ok が返されることを確認するだけです。
- 次に、Django プロジェクトのルート ディレクトリの下に my_celery フォルダーを作成し、上記のceleryディレクトリ
の構成、電子メール タスク、SMS タスクを保存します。config は構成ファイル、main はメイン プログラムです。 - config.py (設定ファイル。ここでは、broker_url と result_backend の 2 つの変数名に特に注意してください。間違ってはいけません)
broker_url = 'redis://127.0.0.1:6379/15'
result_backend = 'redis://127.0.0.1:6379/14'
- task.py (タスク ファイル、セロリ タスクは task.py ファイルに記述する必要があります。他のファイル名は認識されません)
import time
import logging
from my_celery.main import app
log = logging.getLogger("django")
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms(mobile):
"""发送短信"""
print("向手机号%s发送短信成功!"%mobile)
time.sleep(5)
return "send_sms OK"
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms2(mobile):
print("向手机号%s发送短信成功!" % mobile)
time.sleep(5)
return "send_sms2 OK"
- main.py (メインプログラム。セロリアプリを作成し、Django 設定ファイルをロードします)
import os
from celery import Celery
# 创建celery的实例对象,做解耦,不在这里传参broker和backend,通过配置文件进行设置
app = Celery('my_django_celery')
# 把celery和Django进行结合,识别和加载Django的配置文件,注意这里是在环境中加入Django的配置
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celeryPros.settings.dev')
# 通过app对象加载配置,把config.py里面的配置读到celery里面去
app.config_from_object('my_celery.config')
# 加载任务,注意!默认会去目录底下的tasks.py文件里面找,所以tasks.py这个文件命名有特殊意义
app.autodiscover_tasks([
'my_celery.my_email',
'my_celery.my_sms',
])
# 启动Celery的命令
# 强烈建议切换目录到mycelery根目录下启动
# celery -A my_celery.main worker --loglevel=info
- 起動コマンド: celery -A my_celery.main worker --loglevel=info
ここのパスは celery フォルダーの外である必要があることに注意して、コマンドを実行します。my_celery.main を通じてパスを見つけることができます。
上記の構成が完了したら、Django の app01 プロジェクトの views.py 関数に戻ってタスクをトリガーします。
from django.shortcuts import render, HttpResponse
from my_celery.my_sms.tasks import send_sms
from my_celery.my_sms.tasks import send_sms2
from my_celery.my_email.tasks import send_email
from my_celery.my_email.tasks import send_email2
# Create your views here.
def test(request):
# 异步任务
# 1. 声明一个和celery一模一样的任务函数,但是我们可以导包来解决
# send_sms.delay("110")
# send_sms2.delay("119")
# # send_sms.delay() #如果调用的任务函数没有参数,则不需要填写任何内容
# 定时任务
from datetime import timedelta, datetime
time_now = datetime.now()
time_now_utc = datetime.utcfromtimestamp(time_now.timestamp())
time_now_utc_new = time_now_utc + timedelta(seconds=20)
send_email.apply_async(args=['cat', ], eta=time_now_utc_new)
return HttpResponse('ok')
Django プロジェクトを開始し、ブラウザーでパス http://127.0.0.1:8000/test/ にアクセスし、vies view 関数にアクセスしてタスクをトリガーします。