Celery介绍和基本使用
Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery。举几个实例场景中可用的例子:
- 你想对100台机器执行一条批量命令,可能会花很长时间 ,但你不想让你的程序等着结果返回,而是给你返回 一个任务ID,你过一段时间只需要拿着这个任务id就可以拿到任务执行结果, 在任务执行ing进行时,你可以继续做其它的事情。
- 你想做一个定时任务,比如每天检测一下你们所有客户的资料,如果发现今天 是客户的生日,就给他发个短信祝福
Celery 在执行任务时需要通过一个消息中间件来接收和发送任务消息,以及存储任务结果, 一般使用rabbitMQ or Redis,
1.1 Celery有以下优点:
- 简单:一单熟悉了celery的工作流程后,配置和使用还是比较简单的
- 高可用:当任务执行失败或执行过程中发生连接中断,celery 会自动尝试重新执行任务
- 快速:一个单进程的celery每分钟可处理上百万个任务
- 灵活: 几乎celery的各个组件都可以被扩展及自定制
Celery基本工作流程图
简单实用下celery
1.部署环境如下:
user application: 172.16.1.120 安装celery redis(2.10.6版本)模块
celery work: 172.16.1.121 安装celery redis(2.10.6版本)模块
broker: 172.16.1.11 安装redis服务
2.user application和celery work都需要安装celery和redis模块,注意redis模块要用2.10.6版本
pip3 install celery
pip3 install redis==2.10.6
broker安装redis服务如下:
wget http://download.redis.io/releases/redis-4.0.11.tar.gz
tar xf redis-4.0.11.tar.gz
make
make install
cp redis.conf /etc/
vim /etc/redis.conf
#设置远程redis需要的密码
requirepass 123456
#设置redis服务监听的ip地址
bind 0.0.0.0
#设置后台启动
daemonize yes
#启动redis服务
redis-server /etc/redis.conf
3.在user application和celery work端创建相同的python脚本 celery_test.py
[root@goser1 ~]# cat celery_test.py
from celery import Celery
import time
app = Celery('tasks',
broker='redis://:123456@172.16.1.11',
backend='redis://:123456@172.16.1.11'
)
@app.task
def add(x,y):
print("running...",x,y)
return x+y
@app.task
def cmd():
print("cmd runing....")
4.在worker端启动celery worker,在user application端通过redis服务将数据发送给远程worker调用并执行,worker执行完成后再将数据返回到redis服务中,这时候user application端使用get()方法就可以从redis服务中获取worker执行的结果。
#在worker端启动celery worker
[root@goser2 ~]# celery -A celery_test worker -l info
#在user application端调用远程worker
[root@goser1 ~]# python3
>>> from celery_test import add
>>> result = add.delay(32,32)
>>> result.get()
64
5.在redis服务中验证user application和celery work端之间通信的key
[root@linux-node1 ~]# redis-cli -h 127.0.0.1 -a 123456
127.0.0.1:6379> keys *
1) "age"
2) "_kombu.binding.celery.pidbox"
3) "celery-task-meta-c3a0dac5-17e1-4ede-b7a8-9f1f386cdd21"
4) "celery-task-meta-010990c8-a39d-4511-aa71-9c43a95f958b"
5) "celery-task-meta-9bec0926-58f6-4438-8f22-7fbbb2058639"
6.如果work执行的时候很长的话,这时候在user application端通过get()获取可能会卡住,可以通过result = ready()来判断work端是否执行完毕,如果没有执行完成返回的是false,如果执行完成返回的就是true。这样user application端在通过get()获取就没问题了
如何在项目中如何使用celery
1.创建一个项目CeleryProject,在项目中创建__init__.py,celery.py,tasks.py文件
#创建项目
[root@goser2 ~]# mkdir CeleryProject
#在项目中创建celery文件和task文件如下
[root@goser2 CeleryProject]# touch __init__.py
[root@goser2 CeleryProject]# vim celery.py
from __future__ import absolute_import, unicode_literals
#导入的是celery模块的Celery方法,因为上面引用的是绝对路径模块absolute_import
from celery import Celery
app = Celery('proj',
broker='redis://:123456@172.16.1.11',
backend='redis://:123456@172.16.1.11',
#include表示使用项目下的tasks名称定义的事务
include=['CeleryProject.tasks'])
# Optional configuration, see the application user guide.
app.conf.update(
result_expires=3600, #定义worker执行结果的过期时间
)
if __name__ == '__main__':
app.start()
#创建tasks事务模块
[root@goser2 CeleryProject]# vim 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)
2.启动worker并让user application端远程调用work执行的结果
#退回到项目的上级目录
[root@goser2 CeleryProject]# cd ..
#启动一个worker,注意这是对整个项目启动一个worker
[root@goser2 ~]# celery -A CeleryProject worker -l info
#在user application端远程调用worker的执行结果,首先将项目传到user application端
[root@goser2 ~]# tar tf celerypro.tar.gz
[root@goser2 ~]# scp celerypro.tar.gz 172.16.1.120:/root/
#user application端解压项目,并执行
[root@goser1 ~]# tar xf celerypro.tar.gz
[root@goser1 ~]# python3
>>> from CeleryProject import tasks
>>> result = tasks.xsum.delay([23,3,34,5,12,34])
>>> result.get()
111
后台启动celery worker
#后天启动使用multi start 方式,比如启动两个worker为w1和w2
[root@goser2 ~]# celery multi start w1 -A CeleryProject -l info
[root@goser2 ~]# celery multi start w2 -A CeleryProject -l info
#停止后台worker方式为
[root@goser2 ~]# celery multi stop w1
#还是等待worker执行完成后再停止,使用stopwait
[root@goser2 ~]# celery multi stopwait w2
Celery 定时任务
celery支持定时任务,设定好任务的执行时间,celery就会定时自动帮你执行, 这个定时任务模块叫celery beat
1.创建一个定时任务的脚本periodic_task.py
[root@goser2 ~]# vim CeleryProject/periodic_task.py
from __future__ import absolute_import, unicode_literals
from celery.schedules import crontab
from .celery import app
@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)
2.将这个定时任务调价到celery.py文件中,修改如下:
[root@goser2 ~]# cat CeleryProject/celery.py
from __future__ import absolute_import, unicode_literals
from celery import Celery
app = Celery('proj',
broker='redis://:123456@172.16.1.11',
backend='redis://:123456@172.16.1.11',
include=['CeleryProject.tasks','CeleryProject.periodic_task'])
# Optional configuration, see the application user guide.
app.conf.update(
result_expires=3600,
)
if __name__ == '__main__':
app.start()
3.启动worker
[root@goser2 ~]# celery -A CeleryProject worker -l info
4.启动任务调度器 celery beat, 通过任务调度器定时地向worker发送任务
[root@goser1 ~]# celery -A CeleryProject.periodic_task beat -l info
5.当然还可在periodic_task.py脚本中使用配置文件的形式定义定时任务,比如修改periodic_task.py脚本如下:
[root@goser2 ~]# vim CeleryProject/periodic_task.py
from __future__ import absolute_import, unicode_literals
from celery.schedules import crontab
from .celery import app
app.conf.beat_schedule = {
'add-every-10-seconds': {
'task': 'CeleryProject.tasks.add',
'schedule': 10.0,
'args': (16, 16)
},
'add-every-monday-morning': {
'task': 'CeleryProject.tasks.mul',
'schedule': crontab(hour=23, minute=56, day_of_week=2),
'args': (16, 16),
},
}
app.conf.timezone = 'Asia/Shanghai'
@app.task
def test(arg):
print(arg)
接下来启动worker和celery beat,这样就可以根据配置文件的格式来定时地处理事务。
[root@goser2 ~]# celery -A CeleryProject worker -l info
[root@goser1 ~]# celery -A CeleryProject.periodic_task beat -l info
最佳实践之与django结合
django 可以轻松跟celery结合实现异步任务,只需通过以下几步简单配置即可,当然要先在user application端安装django==1.11 redis==2.10.6 celery
1.首先创建一个项目,项目名随意,这里创建一个CeleryTest项目,在项目中创建一个celery.py文件,这个文件和settings文件在同一级目录,目录结构如下:
- CeleryTest/
- CeleryTest/__init__.py
- CeleryTest/settings.py
- CeleryTest/celery.py
- CeleryTest/urls.py
- manage.py
#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', 'CeleryTest.settings')
app = Celery('CeleryTest')
# 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))
2.修改__init__.py文件如下:
#在__init__.py文件中添加如下内容,表示启动django时自动导入celery app
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']
3.在app中创建tasks.py文件,这个名字不能改成其他的名字,因为在celery.py文件中会根据tasks这个文件名称自动发现
- app1/
- tasks.py
- models.py
- app2/
- tasks.py
- models.py
例如在tasks.py中添加一些任务:
# Create your tasks here
from __future__ import absolute_import, unicode_literals
#导入shared_task模块,是为了在一个app中添加的任务在其他app中也可以调用
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)
4.在settings.py文件中配置后端broker信息,比如配置redis服务作为broker
CELERY_BROKER_URL = 'redis://:xg.1158@172.16.1.11'
CELERY_RESULT_BACKEND = 'redis://:xg.1158@172.16.1.11'
5.设置url和view,在启动django后通过定义url触发一个任务
#添加一个url index
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]
#添加一个视图函数
from django.shortcuts import render,HttpResponse
from app01 import tasks
from celery.result import AsyncResult
# Create your views here.
def index(request):
res_ref = tasks.add.delay(40,50)
print("res_ref_id:",res_ref.task_id)
#AsyncResult模块通过task_id取结果
result = AsyncResult(res_ref.task_id)
print(result.status)
#当结果的状态是SUCCESS的话,就可以通过get()取work端执行后的结果的值
if result.status == 'SUCCESS':
final = result.get()
return HttpResponse(final)
return HttpResponse(result.status)
6.最后在linux设备上启动一个worker,注意需要先安装django==1.11 redis==2.10.6 celery等模块,然后将上面的django项目拷贝到linux设备上启动worker
[root@goser2 ~]# unzip CeleryTest.zip
#进入到django项目中启动worker
[root@goser2 ~]# cd CeleryTest
[root@goser2 CeleryTest]# celery -A CeleryTest worker -l info
7.user application端就可以通过url来触发任务,让worker执行了 :http://127.0.0.1:8000/index/
在django中使用计划任务功能
1.首先安装django-celery-beat模块
pip install django-celery-beat
2.在settings.py文件中添加django-celery-beat这个app
[root@goser2 CeleryTest]# vim CeleryTest/settings.py
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'django_celery_beat',
]
3.创建celery-beat的定时任务的表结构,并创建admin管理员账号及启动django服务
[root@goser2 CeleryTest]# python3 manage.py makemigrations
[root@goser2 CeleryTest]# python3 manage.py migrate
[root@goser2 CeleryTest]# python3 manage.py createsuperuser
[root@goser2 CeleryTest]# python3 manage.py runserver 0.0.0.0:9000
4.通过admin设置定时任务
5.创建好定时任务,接下来就可以创建worker和celery beat了,你会发现每隔10秒,beat会发起一个任务消息让worker执行app01.tasks.xsum任务
#创建一个worker
[root@goser2 ~]# cd CeleryTest
[root@goser2 CeleryTest]# celery -A CeleryTest worker -l info
[2018-12-05 16:05:37,658: INFO/MainProcess] Received task: app01.tasks.xsum[54b884e3-8091-47f2-af61-93793f81f8d1]
[2018-12-05 16:05:37,660: INFO/ForkPoolWorker-1] Task app01.tasks.xsum[54b884e3-8091-47f2-af61-93793f81f8d1] succeeded in 0.0006437449919758365s: 551
#创建一个celery beat来触发任务,注意一定要跟上-S django参数
[root@goser2 ~]# cd CeleryTest
[root@goser2 CeleryTest]# celery -A CeleryTest beat -S django -l info
[2018-12-05 16:05:37,617: INFO/MainProcess] Scheduler: Sending due task task test (app01.tasks.xsum)
注意,经测试,每添加或修改一个任务,celery beat都需要重启一次,要不然新的配置不会被celery beat进程读到