tiger运维走向Python开发_Celery分布式任务队列_week25_day84
内容概要
Celery介绍和基本使用
在项目中如何使用celery
启用多个workers
Celery 定时任务
与django结合
通过django配置celery periodic task
一、Celery介绍和基本使用
Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery, 举几个实例场景中可用的例子:
- 你想对100台机器执行一条批量命令,可能会花很长时间 ,但你不想让你的程序等着结果返回,而是给你返回 一个任务ID,你过一段时间只需要拿着这个任务id就可以拿到任务执行结果, 在任务执行ing进行时,你可以继续做其它的事情。
- 你想做一个定时任务,比如每天检测一下你们所有客户的资料,如果发现今天 是客户的生日,就给他发个短信祝福
Celery 在执行任务时需要通过一个消息中间件来接收和发送任务消息,以及存储任务结果, 一般使用rabbitMQ or Redis,后面会讲
1.1 Celery有以下优点:
- 简单:一单熟悉了celery的工作流程后,配置和使用还是比较简单的
- 高可用:当任务执行失败或执行过程中发生连接中断,celery 会自动尝试重新执行任务
- 快速:一个单进程的celery每分钟可处理上百万个任务
- 灵活: 几乎celery的各个组件都可以被扩展及自定制
Celery基本工作流程图

1.2 Celery安装使用
Celery的默认broker是RabbitMQ, 仅需配置一行就可以
broker_url = 'amqp://guest:guest@localhost:5672//'
rabbitMQ 没装的话请装一下,安装看这里 http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#id3
使用Redis做broker也可以
安装redis组件
$ pip install -U "celery[redis]"
配置很容易,只需配置Redis数据库的位置:
app.conf.broker_url = 'redis://localhost:6379/0'
其中URL的格式为:
Redis://:password @ hostname:port / db_number
方案之后的所有字段都是可选的,并且将默认使用端口6379上的localhost,使用数据库0。
如果想获取每个任务的执行结果,还需要配置一下把任务结果存在哪
如果还要存储状态并返回Redis中的执行结果,还需要配置:
app.conf.result_backend = 'redis://localhost:6379/0'
1. 3 Celery的使用
安装celery模块
$ pip install celery
创建一个celery application 用来定义你的任务列表
创建一个任务文件task_lxh.py
#__author: Tiger lee # -*- coding:utf-8 -*- #date: 2017/2/22 #IN Python 3.5 from celery import Celery app = Celery('task', broker='redis://:password@host', backend='redis://:password@host') # password和host均为redis的password和redis运行的机器IP @app.task def add(x, y): print("running...", x, y) return x + y
启动Celery Worker来开始监听并执行任务
$ celery -A task_lxh worker --loglevel=info
调用任务
再打开一个终端, 进行命令行模式,调用任务
>>> from tasks import add >>> add.delay(4, 4)
看你的worker终端会显示收到 一个任务,此时你想看任务结果的话,需要在调用 任务时 赋值个变量
>>> result = add.delay(4, 4)
ready() 方法返回任务是否已完成处理:
>>> result.ready()
False
您可以等待结果完成,但这很少使用,因为它将异步调用转换为同步调用:
>>> result.get(timeout = 1)
8
如果任务引发异常,get()将重新引发异常,但您可以通过指定propagate参数来覆盖此异常:
>>> result.get(propagate = False)
如果任务引发了异常,您还可以访问原始追溯:
>>> result.traceback
二、在项目中如何使用celery
可以把celery配置成一个应用
目录格式如下
proj/__init__.py /celery.py /tasks.py
proj/celery.py内容
from __future__ import absolute_import, unicode_literals from celery import Celery app = Celery('proj', broker='amqp://', backend='amqp://', include=['proj.tasks']) # Optional configuration, see the application user guide. app.conf.update( result_expires=3600, ) if __name__ == '__main__': app.start()
proj/tasks.py中的内容
from __future__ import absolute_import, unicode_literals from .celery import app @app.task def add(x, y): return x + y @app.task def mul(x, y): return x * y @app.task def xsum(numbers): return sum(numbers)
启动worker
$ celery -A proj worker -l info
输出
-------------- celery@Alexs-MacBook-Pro.local v4.0.2 (latentcall) ---- **** ----- --- * *** * -- Darwin-15.6.0-x86_64-i386-64bit 2017-01-26 21:50:24 -- * - **** --- - ** ---------- [config] - ** ---------- .> app: proj:0x103a020f0 - ** ---------- .> transport: redis://localhost:6379// - ** ---------- .> results: redis://localhost/ - *** --- * --- .> concurrency: 8 (prefork) -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker) --- ***** ----- -------------- [queues] .> celery exchange=celery(direct) key=celery
celery远程执行命令并返回结果步骤:
1 . pip install celery 2. 启动redis 3. 创建celry app task_lxh.py from celery import Celery import subprocess,time app = Celery('task', broker='redis://:password@host', backend='redis://:password@host') # password和host均为redis的password和redis运行的机器IP @app.task def add(x, y): print("running...", x, y) return x + y @app.task def run_cmd(cmd): print('run cmd ...',cmd) time.sleep(5) cmd_obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) return cmd_obj.stdout.read().decode("utf-8") 4. celery -A task workery --loglevel=info 5. 生成任务 python3 >>>:import task_lxh >>>:r = task_lxh.cmd_run.delay("df") >>>:r.get()
后台启动worker
在后台
在生产环境中,您希望在后台运行worker,这在守护进程教程中有详细描述。
守护进程脚本使用celery multi命令在后台启动一个或多个工作线程:
$ celery multi start w1 -A proj -l info celery multi v4.0.0 (latentcall) > Starting nodes... > w1.halcyon.local: OK You can restart it too: $ celery multi restart w1 -A proj -l info celery multi v4.0.0 (latentcall) > Stopping nodes... > w1.halcyon.local: TERM -> 64024 > Waiting for 1 node..... > w1.halcyon.local: OK > Restarting node w1.halcyon.local: OK celery multi v4.0.0 (latentcall) > Stopping nodes... > w1.halcyon.local: TERM -> 64052
或停止:
$ celery multi stop w1 -A proj -l info
stop命令是异步的,因此它不会等待worker关闭。 您可能希望使用stopwait命令,这样可以确保在退出之前完成所有当前正在执行的任务:
$ celery multi stopwait w1 -A proj -l info
三、Celery 定时任务
celery支持定时任务,设定好任务的执行时间,celery就会定时自动帮你执行, 这个定时任务模块叫celery beat
from celery import Celery from celery.schedules import crontab app = Celery() @app.on_after_configure.connect def setup_periodic_tasks(sender, **kwargs): # Calls test('hello') every 10 seconds. sender.add_periodic_task(10.0, test.s('hello'), name='add every 10') # Calls test('world') every 30 seconds sender.add_periodic_task(30.0, test.s('world'), expires=10) # Executes every Monday morning at 7:30 a.m. sender.add_periodic_task( crontab(hour=7, minute=30, day_of_week=1), test.s('Happy Mondays!'), ) @app.task def test(arg): print(arg)
add_periodic_task 会添加一条定时任务
上面是通过调用函数添加定时任务,也可以像写配置文件 一样的形式添加, 下面是每30s执行的任务
app.conf.beat_schedule = { 'add-every-30-seconds': { 'task': 'tasks.add', 'schedule': 30.0, 'args': (16, 16) }, } app.conf.timezone = 'UTC'
任务添加好了,需要让celery单独启动一个进程来定时发起这些任务, 注意, 这里是发起任务,不是执行,这个进程只会不断的去检查你的任务计划, 每发现有任务需要执行了,就发起一个任务调用消息,交给celery worker去执行
启动任务调度器 celery beat
$ celery -A periodic_task beat
输出like below
celery beat v4.0.2 (latentcall) is starting. __ - ... __ - _ LocalTime -> 2017-02-08 18:39:31 Configuration -> . broker -> redis://localhost:6379// . loader -> celery.loaders.app.AppLoader . scheduler -> celery.beat.PersistentScheduler . db -> celerybeat-schedule . logfile -> [stderr]@%WARNING . maxinterval -> 5.00 minutes (300s)
此时还差一步,就是还需要启动一个worker,负责执行celery beat发起的任务
启动celery worker来执行任务
$ celery -A periodic_task worker -------------- celery@Alexs-MacBook-Pro.local v4.0.2 (latentcall) ---- **** ----- --- * *** * -- Darwin-15.6.0-x86_64-i386-64bit 2017-02-08 18:42:08 -- * - **** --- - ** ---------- [config] - ** ---------- .> app: tasks:0x104d420b8 - ** ---------- .> transport: redis://localhost:6379// - ** ---------- .> results: redis://localhost/ - *** --- * --- .> concurrency: 8 (prefork) -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker) --- ***** ----- -------------- [queues] .> celery exchange=celery(direct) key=celery
好啦,此时观察worker的输出,是不是每隔一小会,就会执行一次定时任务呢!
注意:Beat需要将任务的最后运行时间存储在本地数据库文件(默认情况下名为celerybeat-schedule)中,因此它需要访问在当前目录中写入,或者您可以为此文件指定自定义位置:
$ celery -A periodic_task beat -s /home/celery/var/run/celerybeat-schedule
更复杂的定时配置
上面的定时任务比较简单,只是每多少s执行一个任务,但如果你想要每周一三五的早上8点给你发邮件怎么办呢?哈,其实也简单,用crontab功能,跟linux自带的crontab功能是一样的,可以个性化定制任务执行时间
linux crontab详解: http://www.cnblogs.com/peida/archive/2013/01/08/2850483.html
from celery.schedules import crontab app.conf.beat_schedule = { # Executes every Monday morning at 7:30 a.m. 'add-every-monday-morning': { 'task': 'tasks.add', 'schedule': crontab(hour=7, minute=30, day_of_week=1), 'args': (16, 16), }, }
上面的这条意思是每周1的早上7.30执行tasks.add任务
还有更多定时配置方式如下:

上面能满足你绝大多数定时任务需求了,甚至还能根据潮起潮落来配置定时任务, 具体看 http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html#solar-schedules
四、最佳实践之与django结合
django 可以轻松跟celery结合实现异步任务,只需简单配置即可
如果你有一个现有的Django项目布局,如:
- proj/ - proj/__init__.py - proj/settings.py - proj/urls.py - manage.py
那么推荐的方法是创建一个新的定义Celery实例的proj/proj/celery.py模块:
file: proj/proj/celery.py
from __future__ import absolute_import, unicode_literals import os from celery import Celery # set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings') app = Celery('proj') # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
然后你需要在你的proj/proj/__init__.py模块导入这个应用程序。 确保在Django启动时加载应用程序,以便@shared_task装饰器(稍后提到)将使用它:
file: proj/proj/__init__.py
from __future__ import absolute_import, unicode_literals # This will make sure the app is always imported when # Django starts so that shared_task will use this app. from .celery import app as celery_app __all__ = ['celery_app']
请注意,此示例项目布局适合较大的项目,对于简单项目,您可以使用定义应用程序和任务的单个包含的模块,如“使用Celery的第一步”教程。
让我们分解在第一个模块中发生了什么,首先从未来导入绝对导入,以便我们的celery.py模块不会与库冲突:
from __future__ import absolute_import
然后为celery命令行程序设置默认的DJANGO_SETTINGS_MODULE环境变量:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')
这行可以不需要,但它可以避免你总是将设置模块传递到celery程序。 它必须始终在创建应用程序实例之前,如:
app = Celery('proj')
这是我们的库的实例。
我们还将Django设置模块添加为Celery的配置源。 这意味着您不必使用多个配置文件,而是直接从Django设置配置Celery; 但你也可以分开他们,如果需要。
大写名称空间表示所有Celery配置选项必须以大写而不是小写形式指定,并以CELERY_开头,例如,task_always_eager`设置将变为CELERY_TASK_ALWAYS_EAGER,并且broker_url设置将变为CELERY_BROKER_URL。
你可以直接在这里传递对象,但使用字符串是更好的,因为那时工人不必序列化对象。
app.config_from_object('django.conf:settings', namespace='CELERY')
接下来,可重用应用程序的常见做法是在单独的tasks.pymodule中定义所有任务,并且Celery有自动发现这些模块的方法:
app.autodiscover_tasks()
使用Celery之上的行将根据tasks.py约定从所有已安装的应用程序中自动发现任务:
- app1/
- tasks.py
- models.py
- app2/
- tasks.py
- models.py
最后,debug_task示例是转储其自己的请求信息的任务。 这是使用Celery 3.1中引入的新的bind = True任务选项来轻松引用当前任务实例。
然后在具体的app里的tasks.py里写你的任务
# Create your tasks here from __future__ import absolute_import, unicode_literals from celery import shared_task @shared_task def add(x, y): return x + y @shared_task def mul(x, y): return x * y @shared_task def xsum(numbers): return sum(numbers)
在你的django views里调用celery task
from django.shortcuts import render,HttpResponse # Create your views here. from bernard import tasks def task_test(request): res = tasks.add.delay(228,24) print("start running task") print("async task res",res.get() ) return HttpResponse('res %s'%res.get())
...
实例: 将Django项目放到linux机器上运行,实现django与linux上的celery协作

上传Django项目到linux上

通过celery来启动Django

启动成功的界面

在linux上面修改Django项目下的APP下的tasks.py

在每一个任务里面加上time.sleep(5)

在index获取到task_id之后,通过ID来取任务的状态,交给views返回到worker进行运算并返回结果SUCCESS



Django目录结构与需要修改的py文件如下:

相关代码:
__init__.py
from __future__ import absolute_import, unicode_literals # This will make sure the app is always imported when # Django starts so that shared_task will use this app. # 这里什么都不用改 from .celery import app as celery_app __all__ = ['celery_app']
selery.py
#__author: Tiger lee # -*- coding:utf-8 -*- #date: 2017/2/22 #IN Python 3.5 from __future__ import absolute_import, unicode_literals import os from celery import Celery # set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Celery_Django.settings') # 设置celery的环境变量 app = Celery('proj') # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix.(名字必须以CELERY_开头) app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
settings.py需要添加连接redis的代码
CELERY_BROKER_URL = "redis://:lxh123456@192.168.10.106" CELERY_RESULT_BACKEND = "redis://:lxh123456@192.168.10.106"
urls.py
from django.conf.urls import url from django.contrib import admin from app_lxh import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index$', views.index), url(r'^task_res/(\w+)/$', views.task_res), ]
APP下的文件:
tasks.py
#__author: Tiger lee # -*- coding:utf-8 -*- #date: 2017/2/22 #IN Python 3.5 # Create your tasks here from __future__ import absolute_import, unicode_literals from celery import shared_task @shared_task def add(x, y): print ("running task add") #这行在把django放到linux里面需要添加 time.sleep(10) #这行在把django放到linux里面需要添加 return x + y @shared_task def mul(x, y): return x * y @shared_task def xsum(numbers): return sum(numbers)
views.py
from django.shortcuts import render, HttpResponse from app_lxh import tasks from celery.result import AsyncResult # Create your views here. def index(request): res = tasks.add.delay(5, 999) print ("res:", res) return HttpResponse(res.task_id) def task_res(request): result = AsyncResult(id="c9fbf4d3-abaf-4eb2-a819-2ea9a1a27582") return HttpResponse(result.status)
五、在django中使用计划任务功能
有一个django-celery-beat扩展,在Django数据库中存储计划,并提供了一个方便的管理界面来管理运行时的周期性任务。
要安装和使用此扩展程序:
1 使用pip安装软件包:
$ pip install django-celery-beat
2 将django_celery_beat模块添加到您的Django项目的settings.py中的INSTALLED_APPS:
INSTALLED_APPS = ( ..., 'django_celery_beat', )
注意:模块名称中没有破折号,只有下划线。
3 应用Django数据库迁移,以便创建必要的表:
$ python manage.py migrate
4 使用django调度程序启动celery beat服务:
$ celery -A proj beat -l info -S django
5 访问Django-Admin界面以设置一些定期任务。
在admin页面里,有3张表

配置完长这样

此时启动你的celery beat 和worker,会发现每隔2分钟,beat会发起一个任务消息让worker执行scp_task任务
注意,经测试,每添加或修改一个任务,celery beat都需要重启一次,要不然新的配置不会被celery beat进程读到
作者:TigerLee
出处:http://www.cnblogs.com/tiger666/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!

浙公网安备 33010602011771号