Django世界-迈出第九步-Celery

Django Celery

前言

  • Celery是一个基于python开发的分布式任务队列,而做python WEB开发最为流行的框架莫属Django,但是Django的请求处理过程都是同步的无法实现异步任务,若要实现异步任务处理需要通过其他方式(前端的一般解决方案是ajax操作),而后台Celery就是不错的选择。倘若一个用户在执行某些操作需要等待很久才返回,这大大降低了网站的吞吐量。
  • kombu : 支持将不同的消息中间件以插件的方式进行灵活配置。Kombu中使用transport这个术语来表示一个具体的消息中间件(后续均用broker指代)
    https://blog.csdn.net/weixin_37947156/article/details/76372427

定义

  • Celery是一个简单、灵活和可靠的分布式系统,可以处理大量的消息,同时提供维护这样一个系统所需的工具。
  • Celery是一个任务队列,关注实时处理,同时支持任务调度。

模块

字段说明
Task(任务模块)包含异步任务和定时任务。其中,异步任务通常在业务逻辑中被触发并发往任务队列,定时任务由 Celery Beat 进程周期性地将任务发往任务队列
Broker(消息中间键)即为任务调度队列,接收任务生产者发来的消息(即任务),将任务存入队列。Celery 本身不提供队列服务,官方推荐使用 RabbitMQ 和 Redis 等
Worker(任务执行单元)是执行任务的处理单元,它实时监控消息队列,获取队列中调度的任务,并执行它。
Backend(任务结果储存)用于存储任务的执行结果,以供查询。同消息中间件一样,存储也可使用 RabbitMQ, Redis 和 MongoDB 等。

Celery定时任务配置

在进行配置前先来看看项目结构:
├── test_celery
│ ├── celery.py
│ ├── init.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── art
│ ├── admin.py
│ ├── apps.py
│ ├── init.py
│ ├── migrations
│ ├── models
│ ├── tasks.py
│ ├── tests.py
│ └── views
└── start-celery.sh

其中art是我们的app,test_celery则是我们的project。我们需要关心的主要是 celery.pysettings.pytasks.pystart-celery.sh

1. celery.py

首先是celery.py,想让celery执行任务就必须实例化一个celery app,并把settings.py里的配置传入app:

 from __future__ import absolute_import, unicode_literals
    import os
    from celery import Celery
    # 设置django环境
    from django.conf import settings

    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_celery.settings')
    broker = 'redis://127.0.0.1:6379/7'
    app = Celery('art_project', broker=broker)

    #  使用CELERY_ 作为前缀,在settings中写配置
    app.config_from_object('django.conf:settings', namespace='CELERY')
    # 发现任务文件每个app下的task.py
    app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

2. init.py

#test_celery/init.py
    import pymysql

    # from __future__ import absolute_import, unicode_literals
    from .celery import app as celery_app

    __all__ = ['celery_app']

    pymysql.install_as_MySQLdb()

3. tasks.py

它应该位于你的app目录中,前面我们配置了自动发现,所以celery会自动找到这些tasks,我们的tasks将写在这一模块中,代码涉及了一些orm的使用:

from __future__ import absolute_import, unicode_literals
    import datetime
    from celery import shared_task
    from test_celery.celery import app

    @app.task(name='add')
    def add(x, y):
        '''触发任务'''
        return x + y


    @shared_task(name='art.tasks.tell')
    def tell():
        print(123)
        print('task test,[%s],args=(%s)' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), None))
        '''定时任务'''
        name = '郭志航'
        age = '36'
        # add.delay(1, 2)
        return '姓名是:' + name + ',年龄是:' + age

4. setting.py

 '''
    1、当djcelery.setup_loader()运行时,Celery便会去查看INSTALLD_APPS下包含的所有app目录中的tasks.py文件,
    找到标记为task的方法,将它们注册为celery task
    ​2、BROKER_URL:broker是代理人,它负责分发任务给worker去执行。我使用的是Redis作为broker

    3、 没有设置 CELERY_RESULT_BACKEND,默认没有配置,此时Django会使用默认的数据库(也是你指定的orm数据库)。

    4、CELERY_IMPORTS:是导入目标任务文件

    5、CELERYBEAT_SCHEDULER:使用了django-celery默认的数据库调度模型,任务执行周期都被存在默认指定的orm数据库中.

    6、CELERYBEAT_SCHEDULE:设置定时的时间配置, 可以精确到秒,分钟,小时,天,周等。
    '''

    #############################
    # celery 配置信息 start
    #############################
    import djcelery

    djcelery.setup_loader()
    BROKER_URL = 'redis://127.0.0.1:6379/7'  # 设置broker为代理人
    # CELERY_IMPORTS = ('art.tasks')

    CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'

    # celery settings
    # 这两项必须设置,否则不能正常启动celery beat
    CELERY_ENABLE_UTC = True
    CELERY_TIMEZONE = 'Asia/Shanghai'
    # 任务队列配置
    CELERY_ACCEPT_CONTENT = ['application/json', ]
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_BROKER_URL = 'redis://127.0.0.1:6379/7'  # 设置broker为代理人
    CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/7'
    from celery.schedules import crontab
    from celery.schedules import timedelta

    # CELERYBEAT_SCHEDULE = {  # 定时器策略
    #     # 定时任务一: 每隔30s运行一次
    #     u'测试定时器1': {
    #         "task": "art.tasks.tell",
    #         # "schedule": crontab(minute='*/2'),  # or 'schedule':   timedelta(seconds=3),
    #         "schedule": timedelta(seconds=30),
    #         "args": (),
    #     },
    # }
    '''
        task:任务函数所在的模块,模块路径得写全,否则找不到将无法运行该任务
        schedule:定时策略,一般使用 celery.schedules.crontab ,上面例子为每小时的0分执行一次任务,具体写法与linux的crontab类似可以参考文档说明
        args:是个元组,给出任务需要的参数,如果不需要参数也可以不写进配置,就像例子中的一样
        其余配置项较少用,可以参考文档

    '''
    CELERY_BEAT_SCHEDULE = {
        u'测试定时器1': {
            'task': 'art.tasks.tell',
            # 'schedule': crontab(minute=0, hour='*/1'),
            'schedule': timedelta(seconds=10),
        }
    }
    #############################
    # celery 配置信息 end
    #############################

5. Views.py

触发任务

 art/views.py
    def add_handler(request):
        x = request.GET.get('x', '1')
        y = request.GET.get('y', '1')

        res = add.delay(int(x), int(y))
        res = {'code': 200, 'num': res.task_id, 'data': [{'x': x, 'y': y}]}
        return HttpResponse(json.dumps(res))

6. start-celery.sh

-A 表示app所在的目录,-B表示启动celery beat运行定时任务。

触发任务: celery -A art.tasks worker -l info
定时任务: celery -A art.tasks.tell beat -l info
定时任务和触发任务同时进行: celery -A art.tasks worker -l info -B
定时任务和触发任务同时进行:

export REDIS_ADDR=127.0.0.1
 
celery -A test_celery worker -l info -B -f /path/to/log

[2018-12-21 13:00:00,022: INFO/MainProcess] Received task: news.tasks.fetch_all_news[e4566ede-2cfa-4c19-b2f3-0c7d6c38690d]
[2018-12-21 13:00:00,046: INFO/MainProcess] Received task: news.tasks.fetch_news[583e96dc-f508-49fa-a24a-331e0c07a86b]
[2018-12-21 13:00:00,051: INFO/ForkPoolWorker-2] Task news.tasks.fetch_all_news[e4566ede-2cfa-4c19-b2f3-0c7d6c38690d] succeeded in 0.02503809699555859s: None
[2018-12-21 13:00:00,052: INFO/MainProcess] Received task: news.tasks.fetch_news[c61a3e55-dd3c-4d49-8d6d-ca9b1757db25]
[2018-12-21 13:00:00,449: INFO/ForkPoolWorker-5] Task news.tasks.fetch_news[c61a3e55-dd3c-4d49-8d6d-ca9b1757db25] succeeded in 0.39487219898728654s: None
[2018-12-21 13:00:00,606: INFO/ForkPoolWorker-3] Task news.tasks.fetch_news[583e96dc-f508-49fa-a24a-331e0c07a86b] succeeded in 0.5523456179944333s: None

7. 遇见的问题

  1. 定时任务 @shared_task(name='art.tasks.tell') 要和 settings.py 中 "task": "art.tasks.tell",一致性
  2. 启动celery后调用任务后报错:
  ERROR/MainProcess Unrecoverable error: AttributeError("'str' object has no attribute 'items'")
解决方案: 将 redis版本降低到 2.10.6 , pip install redis==2.10.6
  1. 启动celery异步任务时, 报: KeyError: ‘backend’
 from . import async, base
    ^
    SyntaxError: invalid syntax
异常原因: Python3.6及其以上的版本已经将async收入关键字中, 而celery4.0版本也用到async, 
如果不配置backend的话是不会有这个异常的.

解决方案: 第一种: 将celery的版本将为2.10.6,  第二种: 将celery依赖中的async更名为其他名称即可

Celery 发送邮件

1. settings.py

 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
    # 发送邮件配置
    # EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_USE_SSL = False
    # smpt服务地址
    EMAIL_HOST = '127.0.0.1'
    EMAIL_PORT = 25
    EMAIL_HOST_USER = ''
    EMAIL_HOST_PASSWORD = ''
    EMAIL_FROM = 'Gavin Z.H. Guo/CEN/FOXCONN'

tasks.py

# 定义任务函数
@app.task(name='send_mail')
def send_register_active_email(username,to_email):
    '''发送激活邮件'''
    # 组织邮件信息
    subject = 'test_type'
    message = 'luke is ugly'
    sender = settings.EMAIL_FROM
    receiver = [to_email, ]
    html_message = '<h1>%s, 欢迎您成为天天生鲜注册会员</h1>收到邮件告诉我一声,Thanks <br/>' % (username)
    send_mail(subject, message, sender, receiver, html_message=html_message)
    time.sleep(5)
    return '发送成功'

views.py

def index(request, *args, **kwargs):
    emial = 'Gavin Z.H. Guo/CEN/FOXCONN'
    name = 'luke'
    print('发送邮件中---')
    send_register_active_email.delay(name,emial)
    print('发送成功-----')
    res = {'status': 200, 'message': 'ok'}
    return HttpResponse(json.dumps(res))
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值