1. 认识 Celery

Celery 是一个 基于 Python 开发的分布式异步消息任务队列,可以实现任务异步处理,制定定时任务等。

  • 异步消息队列:执行异步任务时,会返回一个任务 ID 给你,过一段时间后拿着任务 ID 去取执行结果
  • 定时任务:类似于 Windows / Linux 上的定时任务,到点执行任务

Celery 在执行任务时需要通过一个消息中间件来接收和发送任务消息,以及存储任务结果, 一般使用 rabbitMQRedis(默认采用 RabbitMQ)

优点:

  • 简单易用
  • 高可用:即使执行失败或执行过程发生中断,也会尝试再次执行
  • 快速:一个单进程的 Celery 每分钟可以执行上百万个任务
  • 拓展性强:Celery 的各个组件都可以拓展和自定制

Celery 构成

Celery 主要模块:

  • 任务模块 Task:异步和定时任务
  • 消息中间件 Broker:即任务调度队列,接收生产者发来的任务,将任务存入队列。Celery 本身不提供队列服务,官方推荐 RabbitMQ 或 Redis 等
  • 任务执行单元 Worker:处理任务,实时监控消息队列,获取队列中调度的任务,并执行它。
  • 结果存储 Backend:存储任务执行结果,以便查询,与中间件一样,也可以使用 RabbitMQ、Redis 或 MongoDB 存储

2. 异步任务

实现异步任务步骤:

  • 创建一个 Celery 实例
  • 启动 Celery Worker
  • 应用程序调用异步任务

1、安装

  1. pip3 install 'celery[redis]'
  2. pip3 install celery

2、创建 Celery 实例

C1/tasks.py

  1. # -*- coding: utf-8 -*-
  2. import time
  3. from celery import Celery
  4. broker = 'redis://127.0.0.1:6379' # 消息中间件
  5. backend = 'redis://127.0.0.1:6379/0' # backend ,存储结果
  6. app = Celery('my_task', broker=broker, backend=backend) # 创建实例
  7. # 创建一个任务,5s 后执行
  8. @app.task(name='tasks.add')
  9. def add(x, y):
  10. time.sleep(5) # 模拟耗时操作
  11. return x + y

3、启动 Celery Worker

打开 Ubuntu 终端,输入:celery worker -A C1.tasks --loglevel=info,看到如下图就表示启动成功了:

参数:

  • A:指定实例所在位置
  • --loglevel:指定日志级别,有:warning、debug、info、error、fatal ,默认 warning

4、调用任务

另起一个终端,进入 Python 环境,执行任务:

  1. # Celery 提供两种方法来调用任务,delay() 或 apply_async() 方法
  2. python3
  3. >>> from tasks import add
  4. >>> add.delay(6, 8) # 调用任务,并返回一个任务 ID
  5. <AsyncResult: 194e99af-d0bd-481b-a500-433ec19117e4>

判断任务是否完成:

  1. >>> result = add.delay(6, 8)
  2. >>> result.ready() # True 表示已完成
  3. True

获取任务结果:

  1. >>> result.get()
  2. 14

踩坑:在调用任务时出现Received unregistered task of type 'tasks.add'.

  • 原因:Celery 没有找到读取到任务
  • 解决办法:在装饰器出加上 name='tasks.add'

参考博客:Received unregistered task of type ‘XXX’ Celery报错

3. 项目中使用 celery

celery 还可以配置成一个应用,放置在项目中使用,其结构为:

Tips:

  • 项目应该是个包文件
  • 必须命名为 celery.py,否则报错 AttributeError:module 'proj' has no attribute 'celery'

1、proj/celery.py

  1. from __future__ import absolute_import, unicode_literals # 将相对路径转换为绝对路径
  2. from celery import Celery
  3. # 创建一个Celery的实例
  4. app = Celery('tasks',
  5. # redis://:password@hostname:port/db_number 有密码认证的连接
  6. broker='redis://127.0.0.1:6379',
  7. # broker='redis://:密码@192.168.2.105:6379/0',
  8. backend='redis://127.0.0.1:6379/0', # 用于Celery的返回结果的接收
  9. include=['proj.tasks'] # 用于声明Celery要执行的tasks任务的位置
  10. )
  11. # 配置结果超时时间
  12. app.conf.update(
  13. result_expires=3600, # Celery结果存在中间件Redis的超时时间[仅针对当前的Celery的App]
  14. )
  15. if __name__ == '__main__':
  16. app.start()

2、proj/tasks.py

  1. from __future__ import absolute_import, unicode_literals
  2. from .celery import app # 从我的Celery中导入App
  3. import time
  4. @app.task(name='tasks.add') # 需要配置 name='tasks.add',否则报 Received unregistered task of type 'app.tasks.add'.
  5. def add(x, y):
  6. time.sleep(10)
  7. return x + y
  8. @app.task(name='tasks.mul')
  9. def mul(x, y):
  10. time.sleep(10)
  11. return x * y

3、启动 worker,分为前台和后台启动(无需关心起行为):

  1. # 前台
  2. celery -A proj worker -l info

运行结果如下:

4、调用任务:

  1. # 在这里使用终端调用,也可以再项目中调用
  2. >>> from proj.tasks import add, mul
  3. >>> result1 = add.delay(5, 8)
  4. >>> result2 = mul.delay(5, 8)
  5. >>> result1.get() # 取值
  6. 13
  7. >>> result2.get()
  8. 40

worker 放在后台继续运行,我们可以继续做别的事情:

  1. # w1:worker
  2. celery multi start w1 -A proj -l info # 启动 worker
  3. celery multi restart w1 -A proj -l info # 重启
  4. celery multi stop w1 -A proj -l info # 关闭
  5. ps -ef | grep celery # 查看目前还有几个 worker 正在运行


参考文章

4. 定时任务

celery 通过 celery beat 模块即可实现定时任务功能。

4.1 小试牛刀

1、新建一个 c1\task1.py,编辑如下:

  1. from celery import Celery
  2. from celery.schedules import crontab
  3. app = Celery()
  4. @app.on_after_configure.connect
  5. def setup_periodic_tasks(sender, **kwargs):
  6. # 每过 10 s,执行一次 hello
  7. sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')
  8. # 每过 30 s,执行一次 world
  9. sender.add_periodic_task(30.0, test.s('world'), expires=10)
  10. # 每周一七点三十执行一次 Happy Mondays!
  11. sender.add_periodic_task(
  12. crontab(hour=7, minute=30, day_of_week=1),
  13. test.s('Happy Mondays!'),
  14. )
  15. @app.task
  16. def test(arg):
  17. print(arg)

也可以配置成下面这样,或许更好理解:

  1. # 可以配置多个
  2. app.conf.beat_schedule = {
  3. 'add-every-30-seconds': { # 任务名字
  4. 'task': 'tasks.add', # 执行 tasks 中的 add 函数
  5. 'schedule': 30.0, # 时间,也可以用 timedelta(seconds=20),
  6. 'args': (16, 16) # 参数
  7. },
  8. }
  9. app.conf.timezone = 'UTC' # 时区

2、启动 beat 进程,监控是否有任务:

  1. hj@hj:~/桌面/c1$ celery -A task1 beat

3、启动 worker 执行任务:

  1. hj@hj:~/桌面/c1$ celery -A task1 worker

从上图中可以看到,每过 10s,就会输出一个 hello,每过 30s 输出一个 world,当然这只是几个比较简单的任务示例。

beat 需要将任务的最后运行时间存储在本地数据库文件中(默认名称为 celerybeat-schedule),因此需要访问当前目录中的写入,或者您可以为此文件指定自定义位置:

  1. # beat 运行时,会产生几个文件
  2. hj@hj:~/桌面/c1$ ls
  3. celerybeat.pid celerybeat-schedule __pycache__ task1.py
  4. # 指定文件位置
  5. celery -A task1 beat -s /home/celery/var/run/celerybeat-schedule

4.2 使用 crontab 构建复杂定时任务

如果你只是想每过多少秒输出一个 hello 的话,那么上面的功能就能实现。但是若你想每周一的早上七点半定时发送一封邮件或提醒做什么事的话,那么就只能使用 crontab 才能实现(与 Linux 自带的 crontab功能是一样的)。

  1. from celery.schedules import crontab
  2. from datetime import timedelta
  3. app.conf.beat_schedule = {
  4. # 任务一
  5. 'sum-task':{ # 任务名
  6. 'task':'tasks.add', # 执行 tasks.py 中的 add 函数
  7. 'schedule':timedelta(seconds=20), # 时间
  8. 'args':(5, 6) # 参数
  9. },
  10. # 任务二
  11. 'multi-task': {
  12. 'task': 'tasks.multi',
  13. 'schedule': crontab(hour=4, minute=30, day_of_week=1),
  14. 'args': (3, 4)
  15. }
  16. }

更多关于 crontab

示例 说明
crontab() 每分钟执行一次
crontab(minute=0, hour=0) 每天午夜执行
crontab(minute=0, hour='*/3') 每三个小时执行一次
crontab(minute=0,``hour='0,3,6,9,12,15,18,21') 与上面相同
crontab(minute='*/15') 每 15min执行一次
crontab(day_of_week='sunday') 周日每分钟执行一次
crontab(minute='*',``hour='*',``day_of_week='sun') 与上面相同
crontab(minute='*/10',``hour='3,17,22',``day_of_week='thu,fri') 每周四或周五凌晨3-4点,下午5-6点和晚上10-11点
crontab(minute=0,hour='*/2,*/3') 每过一个小时执行一次, 以下时间除外: 1am, 5am, 7am, 11am, 1pm, 5pm, 7pm, 11pm
crontab(minute=0, hour='*/5') 执行小时可被5整除,比如下午三点(十五点)触发
crontab(minute=0, hour='*/3,8-17') 执行时间能被 2整除,在办公时间 8-17点,每小时执行一次
crontab(0, 0,day_of_month='2') 每个月第二天执行
crontab(0, 0,``day_of_month='2-30/3') 每个偶数日执行
crontab(0, 0,``day_of_month='1-7,15-21') 在本月的第一周和第三周执行
crontab(0, 0,day_of_month='11',``month_of_year='5') 每年5月11日执行
crontab(0, 0,``month_of_year='*/3') 每个季度第一个月执行

参考文章

5. Django 中使用 Celery

5.1 构建简单的异步任务

  1. - project/ # 项目主目录
  2. - app/ # app
  3. - urls.py
  4. - views.py
  5. - tasks.py # celery 任务,名字必须是 tasks.py
  6. - project/ # 项目文件
  7. - __init__.py
  8. - settings.py
  9. - urls.py
  10. - celery.py # 创建 Celery 实例,加载 redis 配置文件
  11. - manage.py

在 Django 中使用 Celery ,依赖 django_celery_beat,因此先要安装它:

  1. pip3 install django_celery_beat

并将其添加到 settings.py 中:

  1. INSTALLED_APPS = [
  2. 'django.contrib.admin',
  3. 'django.contrib.auth',
  4. 'django.contrib.contenttypes',
  5. 'django.contrib.sessions',
  6. 'django.contrib.messages',
  7. 'django.contrib.staticfiles',
  8. 'app',
  9. 'django_celery_beat',
  10. ]
  11. ...
  12. # redis 连接
  13. CELERY_BROKER_URL = 'redis://127.0.0.1:6397'
  14. CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6397/0'

1、project/celery

  1. from __future__ import absolute_import, unicode_literals
  2. import os
  3. from celery import Celery, platforms
  4. # 使用 Django 环境
  5. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Project.settings')
  6. app = Celery('celery_task')
  7. app.config_from_object('django.conf:settings', namespace='CELERY')
  8. # Load task modules from all registered Django app configs.
  9. app.autodiscover_tasks()
  10. # 运行 root 用户运行 celery
  11. platforms.C_FORCE_ROOT = True
  12. @app.task(bind=True)
  13. def debug_task(self):
  14. print('Request: {0!r}'.format(self.request))

2、project/__init__.py

  1. from __future__ import absolute_import, unicode_literals
  2. # 确保导入应用,Django 启动就能使用 app
  3. from .celery import app as celery_app
  4. __all__ = ['celery_app']

3、创建任务 app/tasks.py

  1. from __future__ import absolute_import, unicode_literals
  2. from celery import shared_task
  3. import time
  4. @shared_task
  5. def add(x, y):
  6. time.sleep(10)
  7. return x + y
  8. @shared_task
  9. def multi(x, y):
  10. time.sleep(10)
  11. return x * y

tasks.py 必须在各个 app 根目录下,且只能叫 tasks.py

4、视图中调用任务 views.py

  • ready():判断任务是否执行完毕
  • get(timeout=1):获取结果
  • traceback():获取原始回溯信息
  1. from django.shortcuts import render, HttpResponse
  2. from celery.result import AsyncResult
  3. def celery_test(request):
  4. # 调用任务
  5. task = add.delay(4,22)
  6. return HttpResponse(task.id) # 获取任务 id
  7. def celery_res(request):
  8. # 获取任务结果
  9. task_id = 'b3fbe0da-57bb-4055-aea2-160afd6ae801'
  10. res = AsyncResult(id=task_id)
  11. return HttpResponse(res.get()) # 获取结果

5、路由配置 app/urls.py

  1. path('celery_test/', views.celery_test, name='celery_test'),
  2. path('celery_result/', views.celery_result, name='celery_result'),

6、打开终端启动 worker

  1. celery -A project worker -l info

访问 127.0.0.1:8000/app/celery_test 调用执行任务:

访问 127.0.0.1:8000/app/celery_result 查看任务结果:

因为这是异步处理的,所有再执行任务时,其他代码照样执行。

5.2 在 Django 中使用定时任务

在 Django 也能设置定时任务,依赖于 django_celery_beatcrontab

1、在 project/celery.py 添加定时任务:

  1. from celery.schedules import crontab
  2. from datetime import timedelta
  3. app.conf.update(
  4. CELERYBEAT_SCHEDULE = {
  5. # 任务一
  6. 'sum-task':{
  7. 'task':'app.tasks.add',
  8. 'schedule':timedelta(seconds=20),
  9. 'args':(5, 6)
  10. },
  11. # 任务二
  12. 'multi-task': {
  13. 'task': 'app.tasks.multi',
  14. 'schedule': crontab(hour=4, minute=30, day_of_week=1),
  15. 'args': (3, 4)
  16. }
  17. }
  18. )

在上面添加了两个定时任务 sum-taskmulti-task

  • sum-task :每过 20 s执行一次 add() 函数
  • multi-task:每周一的早上四点三十分执行一次 multi() 函数

启动 celery beat ,celery 启动一个 beat 进程不断检查是否有任务要执行:

  1. celery -A project beat -l info

timedelta

timedelta 是datetime 的一个对象,需要引入 from datatime import timedelta,参数如下:

  • days:天
  • seconds:秒
  • microseconds:微秒
  • milliseconds:毫秒
  • minutes:分钟
  • hours:小时

crontab

  • month_of_year:月份
  • day_of_month:日期
  • day_of_week:周
  • hour:小时
  • minute:分钟

总结

  • 同时启动异步任务和定时任务:celery -A project worker -b -l info
  • 使用 RabbitMQ,配置:broker='amqp://admin:admin@localhost'
  • Celery 长时间运行避免内存泄露,添加配置:CELERY_MAX_TASKS_PER_CHILD = 10

Celery 基本使用的更多相关文章

  1. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  2. celery使用的一些小坑和技巧(非从无到有的过程)

    纯粹是记录一下自己在刚开始使用的时候遇到的一些坑,以及自己是怎样通过配合redis来解决问题的.文章分为三个部分,一是怎样跑起来,并且怎样监控相关的队列和任务:二是遇到的几个坑:三是给一些自己配合re ...

  3. tornado+sqlalchemy+celery,数据库连接消耗在哪里

    随着公司业务的发展,网站的日活数也逐渐增多,以前只需要考虑将所需要的功能实现就行了,当日活越来越大的时候,就需要考虑对服务器的资源使用消耗情况有一个清楚的认知.     最近老是发现数据库的连接数如果 ...

  4. celery 框架

    转自:http://www.cnblogs.com/forward-wang/p/5970806.html 生产者消费者模式 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据 ...

  5. celery使用方法

    1.celery4.0以上不支持windows,用pip安装celery 2.启动redis-server.exe服务 3.编辑运行celery_blog2.py !/usr/bin/python c ...

  6. Celery的实践指南

    http://www.cnblogs.com/ToDoToTry/p/5453149.html Celery的实践指南   Celery的实践指南 celery原理: celery实际上是实现了一个典 ...

  7. Using Celery with Djang

    This document describes the current stable version of Celery (4.0). For development docs, go here. F ...

  8. centos6u3 安装 celery 总结

    耗时大概6小时. 执行 pip install celery 之后, 在 mac 上 celery 可以正常运行, 在 centos 6u3 上报错如下: Traceback (most recent ...

  9. celery 异步任务小记

    这里有一篇写的不错的:http://www.jianshu.com/p/1840035cb510 自己的"格式化"后的内容备忘下: 我们总在说c10k的问题, 也做了不少优化, 然 ...

  10. Celery 框架学习笔记

    在学习Celery之前,我先简单的去了解了一下什么是生产者消费者模式. 生产者消费者模式 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是 ...

随机推荐

  1. 开发过程中,本地分支和远程跟踪分支发生了diverge

    1 git基本概念梳理 1.1 git的工作目录.暂存区和HEAD指向的版本库以及branch的概念 一个branch就是整个产品的一套代码,而工作目录中就是存放的本branch最新的代码,HEAD指 ...

  2. 关于wx.redirectTo、wx.navigateTo失效问题

    问题:在app.json页面中若配置了tabBar,并且要跳转的目标页面也在tabBar中时,那么常用的几种页面跳转方式便失效了.即不能跳转到tabBar中定义的页面. 解决办法:若要跳转至tabBa ...

  3. Android Development Note-02

    输入框左侧的logo:android:drawableleft   弹出提示: Toast.makeText(this,"提示",Toast.LENGHT_LONG).show() ...

  4. 【Windows】修改远程桌面端口号

    echo off echo 修改远程连接端口 reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Se ...

  5. node版本管理工具 -- nvm安装与使用

    新老项目维护时node环境切换麻烦怎么办? 不用担心,有了nvm ,一个命令就能切换node版本. 首先需要安装nvm工具,进入下载地址. 下载之后安装nvm. nvm安装之后还需要配置两个环境变量( ...

  6. spring-boot-starter-parent

    在官方文档的第三部分的13块讲述了引用的管理,官方推荐的是使用Maven和Gradle. 我一直在用的是maven,而且使用maven有些优势–spring-boot-starter-parent,这 ...

  7. C# 计时器 以“天时分秒毫秒”形式动态增加显示

    参考:http://zhidao.baidu.com/link?url=j-jxQJenrO54BSKJ_IkXWbhdDqbVLUyyenjjSGs8G0xdisgBZ0EMhzyWgARSFct6 ...

  8. 用python 实现录入学生作业情况的小程序

    写一个录入学生作业情况的一个程序 1.查看学生作业情况 2.录入学生作业情况 3.可以让输入3次,需要为空的情况 homeworks = { ‘张流量’: {‘2018.3.22’:”未交”,’201 ...

  9. mongodb 常用操作符

    最近常用mongodb数据库,但是很多操作符不清楚或不知道,所有抽空根据手册整理下,以便于以后查阅(基于3.4版本) 1.查询和投影操作符 1.1比较操作符 $eq 匹配字段值等于指定值的文档 { & ...

  10. python爬虫知识点总结(七)PyQuery详解

    官方学习文档:http://pyquery.readthedocs.io/en/latest/api.html 一.什么是PyQuery? 答:强大有灵活的网页解析库,模仿jQuery实现.如果你觉得 ...