以下代码软件版本为:
Python 3.7.3
redis 3.2.1(pip3 install redis,用来连接redis数据库)
celery 4.3.0
redis数据库:redis-5.0.5
目录结构
注意:修改celery_profile、celery_tasks、celery_schedule都需要重启celery对应服务,求大神告知,添加任务和定时不重启服务的办法。
# 四个文件,都在celery文件夹里
celery
|
celery_profile.py
celery_tasks.py
celery_schedule.py
send_tasks.py
celery_profile.py
from celery import Celery
from kombu import Exchange, Queue
app = Celery('test',
broker='redis://:[email protected]/10',
backend='redis://:[email protected]/11',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
)
# 看了一篇文章说,如果使用redis做broker,exchange可以不配置;但如果使用rabbitMQ做broker,就必须要配置。
# 如果不配置队列,下面命令可以注释掉(简单的环境,建议不要配置)。
queue = (
Queue('default', exchange=Exchange('default', type='direct'), routing_key='default'),
Queue('app_task1', exchange=Exchange('app_task1', type='direct'), routing_key='app_task1'),
Queue('app_task2', exchange=Exchange('app_task2', type='direct'), routing_key='app_task2'),
)
# 一旦配置了route后,所有的任务名都必须要指定route,否则任务无法执行。
# 经过测试,route匹配是最长匹配规则。
'''
注意:
以下的配置,app_task1队列worker,不会处理task2任务。
但queue default队列的worker,会处理所有队列的任务。
也就是当队列app_task2的worker全部坏了,queue default的worker会处理这个队列的任务。
worker启动时,不指定队列,即不加-Q,那默认就属于这个队列,既可以为所有队列提供worker服务。默认队列名称为celery。
'''
route = {
# 匹配任务名以task开头的任何任务。
'task*': {'queue': 'app_task1', 'routing_key': 'app_task1'},
# 精确匹配任务名称。task2名称的任务,就匹配这条路由。
'task2': {'queue': 'app_task2', 'routing_key': 'app_task2'},
# 其它的任务名称,匹配这条路由
# 如果以上队列的worker服务器坏了,这些任务会被全部放进这个队列里,该队列的worker将继续处理这些任务
# 下面这条队列一定要配置,否则其它任务无法处理。
'*': {'queue': 'default', 'routing_key': 'default'},
}
app.conf.update(
task_serializer='json',
# Ignore other content
accept_content=['json'],
result_serializer='json',
# 指定时区,不指定默认为 ‘UTC’
timezone='Asia/Shanghai',
enable_utc=False,
# 任务结果一小时内没有获取则过期,缺省一天.
# result_expires=3600,
# Number of CPU cores.机器缺省几核,这个数字就是多少.建议不要配置
# worker_concurrency=4,
# 如果不配置队列,下面两条命令可以注释掉(简单的环境,建议不要配置)。
task_queues=queue,
task_routes=route,
)
celery_tasks.py
from celery_profile import app
import time
# @app.task(name="task1", time_limit=10) ,使用time_limit可以限制任务运行的时长,
# 超过这个时间任务没运行结束,进程会被强行杀掉重启,然后跳过这个任务,执行下一个任务.
@app.task(name='task1')
def celery_task1(arg):
return 'celery_task1,执行结果为:%s' % arg
@app.task(name="task2")
def celery_task2(arg):
return 'celery_task2,执行结果为:%s' % arg
# 做定时任务
@app.task(name='schedule_add')
def schedule_add(x, y):
print('定时任务开始运行')
x + y
return x + y
celery_schedule.py
from celery_profile import app
from celery.schedules import crontab
app.conf.beat_schedule = {
'add-every-monday-morning': {
'task': 'schedule_add',
'schedule': 1.0,
'args': (16, 16),
},
}
"""
可以使用crontab,增加定时灵活度
比如:
'schedule': crontab(hour=7, minute=30, day_of_week=1),
"""
send_tasks.py
from celery_tasks import celery_task1
from celery_tasks import celery_task2
from time import sleep
from datetime import timedelta
from datetime import datetime
result_1 = celery_task1.delay("第一个任务执行.")
print(result_1.id)
result_2 = celery_task2.delay("第二个任务执行.")
print(result_2.id)
# result_1 = celery_task1.apply_async(args=['第一个任务执行', ], countdown=15)
# print(result_1.id)
#
# result_1 = celery_task1.apply_async(args=['第一个任务执行', ], eta=datetime.now() + timedelta(seconds=10))
# print(result_1.id)
开启worker服务:
开启两个worker服务器,分别处理两个队列(-Q 后面加队列名称,指定worker处理的队列名称):
注意:非本队列的任务,这台worker就不会处理了。
celery worker -A celery_tasks -l info -n bruce1 -Q app_task1
celery worker -A celery_tasks -l info -n bruce2 -Q app_task2
# 后台启动方法为:
celery multi start logging/celery -A celery_tasks -l info -n bruce1 -Q app_task1
注意:后台启动,看不到下面这段话:
celery beat v4.3.0 (rhubarb) is starting.
__ - ... __ - _
LocalTime -> 2019-07-17 01:25:48
Configuration ->
. broker -> redis://:**@192.168.0.100:6379/10
. loader -> celery.loaders.app.AppLoader
. scheduler -> celery.beat.PersistentScheduler
. db -> celerybeat-schedule
. logfile -> [stderr]@%INFO
. maxinterval -> 5.00 minutes (300s)
我们同样也可以通过apply_aynsc()方法来设置任务发送到那个队列中:
这里配置任务加入队列,要高于route配置的。
result_1 = celery_task1.apply_async(queue=‘app_task2’)
我们也可设置一个worker服务器处理两个队列中的任务:
celery worker -A celery_tasks -l info -n bruce3 -Q app_task1,app_task2
如果启动不指定-Q队列,将处理所有队列任务。
celery worker -A celery_tasks -l info -n bruce4
启动之后,能够看到下面这么一段话:
-------------- [queues]
.> app_task1 exchange=app_task1(direct) key=app_task1
.> app_task2 exchange=app_task2(direct) key=app_task2
.> default exchange=default(direct) key=default
启动定时器:
celery beat -A celery_schedule -l info -f logging/schedule_tasks.log
-f logging/schedule_tasks.log:定时任务日志输出路径
后台运行,加参数"–detach":
celery beat -A celery_schedule -l info -f logging/schedule_tasks.log --detach
启动生产者:
python3 send_tasks.py
下面这种配置,代码要比上面简单很多。在应用上,主要的区别是,缺省队列worker,不处理所有队列的任务,只负责缺省任务,即celery队列。
在celery_profile.py里,注释以上route queue代码,添加如下代码:
route = {
# 匹配任务名以task开头的任何任务。
'task*': {'queue': 'queue1'},
# 精确匹配任务名称。task2名称的任务,就匹配这条路由。
# 队列queue1,不会处理task2的任务。
'task2': {'queue': 'queue2'},
# 其它任务名称,全部放进celery队列里。这条代码不配置也可以,因为默认任务都在celery队列。
# 注意:与上面的route配置不同,这种方式的配置,缺省队列的worker,不会对其它队列做任务处理。
# '*': {'queue': 'celery'},
}
app.conf.update(
# 如果不配置队列,下面命令可以注释掉(简单的环境,建议不要配置)。
task_routes=route,
)
启动worker:
celery worker -A celery_tasks -l info -n bruce4
可以看到如下这条信息,意味着只处理celery队列的任务
[queues]
.> celery exchange=celery(direct) key=celery