• Celery框架


    摘要:

    在了解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
    
  • 相关阅读:
    web应用模式
    Win10升级本地Django版本,以及DRF框架的安装使用
    DRF作业(1)
    中间件作业
    复习day03作业
    python基础复习
    利用自定义中间件来实现DRF框架中的request对象的data属性
    复习Day02(python中魔法方法,cookie/sesison/token,pymysql)
    排序之快排
    排序之归并
  • 原文地址:https://www.cnblogs.com/xzqpy/p/11176674.html
Copyright © 2020-2023  润新知