摘要:

在了解celery的异步任务队列之前我们来先了解下Node 似乎我与人聊过了node.js这个框架,他是一个简单运行将js运行在服务器上的

非阻塞的异步框架,一个线程就可以跑起整个项目,但是我想强调的是他的

异步非阻塞和事件驱动!他是怎么把通过一个线程处理这么多请求的,处理一个请求不会阻塞吗?请听我一一道来!

nodejs最大的优势在于一个请求过来,他接入请求,并处理他,当事件遇到一些io操作的时候,就把他的事件丢在一个队列里面,我们称之为

队列(其实并不是栈区,栈是先进后出的概念,我们说他是队列),然后依次排队处理栈,等处理完之后又继续处理请求,栈区发出一个完成信号的时候,继续处理栈区内容,就做到异步非阻塞,而他的事件驱动就是这个队列区完成io操作的信号驱使。

  Celery 是 Distributed Task Queue,分布式任务队列,分布式决定了可以有多个 worker 的存在,队列表示其是异步操作,即存在一个产生任务提出需求的工头,和一群等着被分配工作的码农。

        在 Python 中定义 Celery 的时候,我们要引入 Broker,中文翻译过来就是“中间人”的意思,在这里 Broker 起到一个中间人的角色。在工头提出任务的时候,把所有的任务放到 Broker 里面,在 Broker 的另外一头,一群码农等着取出一个个任务准备着手做。

 

        这种模式注定了整个系统会是个开环系统,工头对于码农们把任务做的怎样是不知情的。所以我们要引入 Backend 来保存每次任务的结果。这个 Backend 有点像我们的 Broker,也是存储任务的信息用的,只不过这里存的是那些任务的返回结果。我们可以选择只让错误执行的任务返回结果到 Backend,这样我们取回结果,便可以知道有多少任务执行失败了。

 

        Celery(芹菜)是一个异步任务队列/基于分布式消息传递的作业队列。它侧重于实时操作但对调度支持也很好。Celery用于生产系统每天处理数以百万计的任务。Celery是用Python编写的,但该协议可以在任何语言实现。它也可以与其他语言通过webhooks实现。Celery建议的消息队列是RabbitMQ,但提供有限支持Redis, Beanstalk, MongoDB, CouchDB, 和数据库(使用SQLAlchemy的或Django的 ORM) 。Celery是易于集成Django, Pylons and Flask,使用 django-celery, celery-pylons and Flask-Celery 附加包即可。

 

什么是celery

目前不支持win平台,但是我们处理后依旧可以使用

聊完这个nodejs这个东西之后,我们引出了一个celery这个框架,他和nodejs相似但是他是一个简单的可靠的分布式系统

他专注于实时处理异步任务队列 同时也支持任务调度

celery三部分

消息中间件   不是celery的自己创立而是第三方  redis(也支持在中间件里)

 

broker是一个消息传输的中间件,可以理解为一个邮箱(概念)。每当应用程序调用celery的异步任务的时候,会向broker传递消息,而后celery的worker将会取到消息,进行对于的程序执行(怎么执行的)。好吧,这个邮箱可以看成是一个消息队列。其中Broker的中文意思是 经纪人 ,其实就是一开始说的 消息队列 ,用来发送和接受消息。这个Broker有几个方案可供选择:RabbitMQ (消息队列),Redis(缓存数据库),数据库(不推荐),等等

 

执行单元  工人 处理执行单元任务的函数

消息结果存储 可以理解数据库 同样的用第三方

 

我们在哪里用?

说实在的我们标题就能理解出 他是异步框架 所以适合大量io密集型的场景(或耗时任务

已经他自己支持定时调度 比如每天凌晨把日志同步起来 或者更新下缓存

好牛逼 在python咋用?

 

pip install celery  下载这个框架 不建议使用Django的

定义执行任务的函数

#把我们的函数装饰为异步执行任务
import celery
import time
# broker='redis://127.0.0.1:6379/2' #版本不加密码
backend='redis://:123456@127.0.0.1:6379/1' #任务提交后放在这个库里
broker='redis://:123456@127.0.0.1:6379/2' #任务执行完后结果的db
cel=celery.Celery('test',backend=backend,broker=broker) 
#装饰函数
@cel.task
def add(x,y):
    return x+y

 

 创造一个可以开启任务功能的模块

from celery_app_task import add
result = add.delay(4,5) #4,5为参数  #启动任务
print(result.id)  #拿到这个用于查看任务的CDK

启动我们的功能

celery worker -A celery_app_task -l info  其他平台
celery worker -A celery_app_task -l info -P eventlet #window

函数启动
from celery_app_task import cel
if __name__ == '__main__':
    cel.worker_main()

查看结果

async = AsyncResult(id="e919d97d-2938-4d0f-9265-fd8237dc2aa3", app=cel)#r任务序列号

if async.successful():
    result = async.get()
    print(result)
    # result.forget() # 将结果删除
elif async.failed():
    print('执行失败')
elif async.status == 'PENDING':
    print('任务等待中被执行')
elif async.status == 'RETRY':
    print('任务异常后正在重试')
elif async.status == 'STARTED':
    print('任务已经开始被执行')

多任务结果

可能用户任务 和订单任务是分开的 所以我们需要多任务 ,之前一个肯定是不行了

pro_cel
    ├── celery_task# celery相关文件夹
    │   ├── celery.py   # celery连接和配置相关文件,必须叫这个名字
    │   └── tasks1.py    #  所有任务函数
    │    └── tasks2.py    #  所有任务函数
    ├── check_result.py # 检查结果
    └── send_task.py    # 触发任务

任务文件

import time
from celery_task.celery import cel

#test1.py
@cel.task
def test_celery(res):
    time.sleep(5)
    return "test_celery任务结果:%s"%res

#test2.py
import time
from celery_task.celery import cel
@cel.task
def test_celery2(res):
    time.sleep(5)
    return "test_celery2任务结果:%s"%res

 celery.py 任务分发作用

from celery import Celery

cel = Celery('celery_demo',
             broker='redis://127.0.0.1:6379/1',
             backend='redis://127.0.0.1:6379/2',
             # 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
             include=['celery_task.tasks1',
                      'celery_task.tasks2'
                      ])

# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False

拿到任务CDK

from celery_task.tasks1 import test_celery
from celery_task.tasks2 import test_celery2

# 立即告知celery去执行test_celery任务,并传入一个参数
result = test_celery.delay('第一个的执行')
print(result.id)
result = test_celery2.delay('第二个的执行')
print(result.id)

查看结果

from celery.result import AsyncResult
from celery_task.celery import cel

async = AsyncResult(id="08eb2778-24e1-44e4-a54b-56990b3519ef", app=cel)  #任务序列号

if async.successful():
    result = async.get()
    print(result)
    # result.forget() # 将结果删除,执行完成,结果不会自动删除
    # async.revoke(terminate=True)  # 无论现在是什么时候,都要终止
    # async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终止。
elif async.failed():
    print('执行失败')
elif async.status == 'PENDING':
    print('任务等待中被执行')
elif async.status == 'RETRY':
    print('任务异常后正在重试')
elif async.status == 'STARTED':
    print('任务已经开始被执行')

开启work

celery worker -A celery_task -l info  -P  eventlet

开启任务

send_task.py

检查任务执行结果

(执行check_result.py)

定时任务

本质:使用apply_async并设定时间    参数eta传递要执行的事件

from celery_app_task import add
from datetime import datetime

# 方式一
# v1 = datetime(2019, 2, 13, 18, 19, 56)
# print(v1)
# v2 = datetime.utcfromtimestamp(v1.timestamp())
# print(v2)
# result = add.apply_async(args=[1, 3], eta=v2)
# print(result.id)

# 方式二
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
from datetime import timedelta
time_delay = timedelta(seconds=10)
task_time = utc_ctime + time_delay

# 使用apply_async并设定时间
result = add.apply_async(args=[4, 3], eta=task_time)
print(result.id)

像Linux的contab任务

本质cel.conf.beat_schedule = { ‘任务1’:{‘tas’:'任务',schedule:"执行频率",args:“参数”  } ,‘任务二’:{} }

 

 

from datetime import timedelta
from celery import Celery
from celery.schedules import crontab

cel = Celery('tasks', broker='redis://127.0.0.1:6379/1', backend='redis://127.0.0.1:6379/2', include=[
    'celery_task.tasks1',
    'celery_task.tasks2',
])
cel.conf.timezone = 'Asia/Shanghai'
cel.conf.enable_utc = False

cel.conf.beat_schedule = {
    # 名字随意命名
    'add-every-10-seconds': {
        # 执行tasks1下的test_celery函数
        'task': 'celery_task.tasks1.test_celery',
        # 每隔2秒执行一次
        # 'schedule': 1.0,
        # 'schedule': crontab(minute="*/1"),
        'schedule': timedelta(seconds=2),
        # 传递参数
        'args': ('test',)
    },
    # 'add-every-12-seconds': {
    #     'task': 'celery_task.tasks1.test_celery',
    #     每年4月11号,8点42分执行
    #     'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
    #     'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
    #     'args': (16, 16)
    # },
}

启动beat:

celery beat -A celery_task -l info

启动work:

celery worker -A celery_task -l info -P  eventlet

 

Django 引入celeary

在项目目录下创建celeryconfig.py

import djcelery
djcelery.setup_loader()
CELERY_IMPORTS=(
    'app01.tasks',
)
#有些情况可以防止死锁
CELERYD_FORCE_EXECV=True
# 设置并发worker数量
CELERYD_CONCURRENCY=4
#允许重试
CELERY_ACKS_LATE=True
# 每个worker最多执行100个任务被销毁,可以防止内存泄漏
CELERYD_MAX_TASKS_PER_CHILD=100
# 超时时间
CELERYD_TASK_TIME_LIMIT=12*30

settings.py

INSTALLED_APPS = [
    ...
    'djcelery',
    'app01'
]
from djagocele import celeryconfig
BROKER_BACKEND='redis'
BOOKER_URL='redis://127.0.0.1:6379/1'
CELERY_RESULT_BACKEND='redis://127.0.0.1:6379/2'

 任务.py

from celery import task
@task
def sa():
    pass

 

server/clery.py

import celery
import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings")

import django
django.setup()

app = celery.Celery(main="celery_app", broker='redis://127.0.0.1:6379/2')

app.autodiscover_tasks(['api'])

celery -A server.celery  worker -l info

 

from server.celery import app

@app.task
def Add(x, y):
    print("asdasda")
    return x + y

 

posted on 2019-07-12 16:08  谢Rain  阅读(603)  评论(1编辑  收藏  举报