Celery分布式文件队列

Celery介绍和基本使用 

Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery。举几个实例场景中可用的例子:

  1. 你想对100台机器执行一条批量命令,可能会花很长时间 ,但你不想让你的程序等着结果返回,而是给你返回 一个任务ID,你过一段时间只需要拿着这个任务id就可以拿到任务执行结果, 在任务执行ing进行时,你可以继续做其它的事情。 
  2. 你想做一个定时任务,比如每天检测一下你们所有客户的资料,如果发现今天 是客户的生日,就给他发个短信祝福

Celery 在执行任务时需要通过一个消息中间件来接收和发送任务消息,以及存储任务结果, 一般使用rabbitMQ or Redis,

1.1 Celery有以下优点:

  1. 简单:一单熟悉了celery的工作流程后,配置和使用还是比较简单的
  2. 高可用:当任务执行失败或执行过程中发生连接中断,celery 会自动尝试重新执行任务
  3. 快速:一个单进程的celery每分钟可处理上百万个任务
  4. 灵活: 几乎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进程读到

posted @ 2018-12-04 21:36  goser  阅读(261)  评论(1编辑  收藏  举报