Celery介绍以及使用

文章目录

  • celery
    • 一、什么是celery
      • 1、celery是什么:
      • 2、使用场景
      • 3、Celery的优点
      • 4、Celery的安装
    • 二、Celery执行异步任务
      • 1、创建异步任务执行文件(消费者)
      • 2、创建生产者文件
      • 3、创建result文件去获取任务函数的返回值,从而查看任务执行结果
      • 4、多目录结构
      • 5、异步任务和定时任务
      • 6、多目录结构下celery执行
    • 三、Django中使用celery
      • 注意点
      • 概念介绍
      • 总体流程
      • 文件对应的代码

celery

转载地址:https://www.cnblogs.com/pyedu/p/12461819.html
视频讲解:https://www.bilibili.com/video/BV1Pa4y1Y7QN?p=1

一、什么是celery

1、celery是什么:

  • celery是一个分布式队列的管理工具

  • celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调用

  • Celery介绍以及使用_第1张图片

  • Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker),任务执行结果存储(task result store)

2、使用场景

  • celery是一个强大的分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)
  • 异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
  • 定时任务:定时执行某件事情,比如每天数据统计

3、Celery的优点

  • Simple简单:因为Celery作为一个对并发技术做了封装的框架,给别人使用和维护都不能做的很复杂,别人也不用太去了解底层的东西。
  • Highly Available高可用:在分布式系统下,一定要做到高可用,保证分布式服务器中的程序对外供给的时间有一个最大的利用率,还要做到稳定
  • Fast快速:单个Celery进程每分钟可以处理百万级的任务
  • Flexible灵活:Celery几乎每个部分都可以扩展使用,自定义池实现、序列化、压缩方案、日志记录、调度器、消费者、生产者、broker传输等等。由于语法上调用比较简单,比如要将消息中间件由RabbitMQ换成Redis,则只需要去配置文件修改一下对应的IP和端口即可,其他配置都不太需要更改

4、Celery的安装

  • pip install -U Celery
    
  • sudo easy_install Celery
    

二、Celery执行异步任务

Celery介绍以及使用_第2张图片

Celery介绍以及使用_第3张图片

1、创建异步任务执行文件(消费者)

  • celery_task.py

    • import celery
      import time
      # backend(后台),就是task result store
      backend = 'redis://127.0.0.1:6379/1'
      # broker 消息中间件
      broker = 'redis://127.0.0.1:6379/2'	
      # 实例化一个celery对象,test只是个名字,可以任意改
      cel = celery.Celery('test', backend=backend, broker=broker)
      
      @cel.task # 使用celery中的装饰器将函数装饰成celery任务(可能是异步任务,可能是定时任务)
      def send_mail(name):
          print('向%s 发送邮件...'%name)
          time.sleep(5)
          print('向%s 发送邮件完成'%name)
          return 'ok'
      
      @cel.task
      def send_msg(name):
          print('向%s 发送短信...' % name)
          time.sleep(5)
          print('向%s 发送短信完成' % name)
          return 'ok'
      
  • 在命令行中使用命令去启动异步任务执行文件,使worker去监听消息队列,若消息队列中有celery任务,则会去执行celery任务

    • celery worker -A clery_task -l info
      

2、创建生产者文件

生产者文件才是celery框架的主文件,当运行对应的文件时,生产者会将celery任务放入消息队列中。

  • produce_task.py

    • from celery_task import *
      result = send_mail.delay('hhh')#这里的result的值并不是send_mail函数的返回值ok,而是该函数的对象,返回值ok存储在backend中
      print(result.id)  
      result2 = send_msg.delay('ggg')
      print(result2.id)
      

3、创建result文件去获取任务函数的返回值,从而查看任务执行结果

  • result.py

    • from celery.result import AsyncResult
      from celery_task import cel
      
      #id为函数对象中的id,要查询哪一个函数的结果就输入哪一个函数的对象对应的id
      async_result = AsyncResult(id='531248c5-75ac-46af-8f01-889da554d7d6', app=cel)
      
      if async_result.successful():
          result = async_result.get()
          print(result)
          # result.forget()  # 将结果删除
      elif async_result.failed():
          print('执行失败')
      elif async_result.status == 'PENDING':
          print('任务等待中被执行')
      elif async_result.status == 'RETRY':
          print('任务异常后正在重启')
      elif async_result.status == 'STARTED':
          print('任务已经开始被执行')
      

4、多目录结构

  • 个人理解:多目录结构其实就是执行多个任务的意思,执行多个任务自然需要把对应的逻辑代码进行解耦,下面所说的单目录结构其实没有这种说法,只是个人为了与多目录结构多比较才这样说的,大概意思是把所有逻辑代码文件都放在一个目录下,这样耦合程度高,不好做代码的维护。

  • 多目录结构相对于单目录结构的优势在于解耦程度高,代码发生错误时好维护

  • 多目录结构中的代码和单目录结构是一样的,注意celery_tasks是一个包结构

  • Celery介绍以及使用_第4张图片
    Celery介绍以及使用_第5张图片

  • celery.py:编写一些配置选项

    • import celery
      backend = 'redis://127.0.0.1:6379/1'
      broker = 'redis://127.0.0.1:6379/2'
      	
      cel = celery.Celery('test',
                          backend=backend,
                          broker=broker,
                          include=['celery_tasks.task01','celery_tasks.task02'])
      
      # 时区
      # cel.conf.timezone = 'Asia/Shanghai'
      # 是否使用UTC
      # cel.conf.enable_utc = False
      
  • task01.py:任务1

    • from celery_tasks.celery import cel
      import  time
      
      @cel.task
      def send_mail(name):
          print('发送邮件 %s' % name)
          time.sleep(5)
          return '邮件发送成功'
      
      
  • task02.py:任务2

    • from celery_tasks.celery import cel
      import time
      
      @cel.task
      def send_msg(name):
          print('发送短信 %s'%name)
          time.sleep(5)
          return '短信发送成功'
      
  • produce_task.py:主函数(生产者函数)

    • from celery_tasks.task01 import send_mail
      from celery_tasks.task02 import send_msg
      result = send_mail.delay('hhh')
      print(result.id)
      result2 = send_msg.delay('ggg')
      print(result2.id)
      
  • check_result.py:检查结果的函数

    • from celery.result import AsyncResult
      from celery_tasks.celery import cel
      
      async_result = AsyncResult(id='4e4ad94a-5dc2-4939-b461-830ed168a0a0', app=cel)
      
      if async_result.successful():
          result = async_result.get()
          print(result)
          # result.forget()  # 将结果删除
          # async_result.revoke(terminate=True)  # 无论现在是什么时候,都要终止
          # async_result.revoke(terminate=False) # 如果现在还没有开始执行呢,那么就可以种植
      elif async_result.failed():
          print('执行失败')
      elif async_result.status == 'PENDING':
          print('任务等待中被执行')
      elif async_result.status == 'RETRY':
          print('任务异常后正在重启')
      elif async_result.status == 'STARTED':
          print('任务已经开始被执行')
      

5、异步任务和定时任务

  • 异步任务是在同一时刻以异步的方式去执行celery任务,而定时任务是指定好了具体时间或者指定延迟去执行celery任务

  • 异步任务使用delay,定时任务使用apply_async,两者函数区别在于,apply_async中有eta参数,可以将时间对象传入,从而达到定时执行任务

  • 单目录结构都是写在生产者函数中

    • # 异步任务
      from celery_tasks.task01 import send_mail
      from celery_tasks.task02 import send_msg
      result = send_mail.delay('hhh')
      print(result.id)
      result2 = send_msg.delay('ggg')
      print(result2.id)
      
      # 定时任务:相对于异步任务来说只是添加了一个定时的时间和用apply_async替换delay方法
      from datetime import datetime, timedelta
      from celery_tasks.task01 import send_mail
      from celery_tasks.task02 import send_msg
      
      # 方式一:指定具体的时间去定时执行celery任务
      # dt为指定的具体时间对象
      dt = datetime(year=2020, month=8, day=2, hour=15, minute=8,second=0)
      # 默认使用utc时间
      dt_utc = dt.utcfromtimestamp(dt.timestamp())
      result = send_mail.apply_async(args=['Bob'], eta=dt_utc)
      print(result.id)
      
      # 方式二:指定一个delay时间,使celery任务延迟多久才去执行
      current_time = datetime.now()
      ct_utc = current_time.utcfromtimestamp(current_time.timestamp())
      time_delay = timedelta(seconds=10)
      task_time = ct_utc + time_delay
      
      result2 = send_msg.apply_async(args=['Michael'], eta=task_time)
      print(result2.id)
      

6、多目录结构下celery执行

  • 单目录结构下的执行步骤应该是:

    • 1、创建对应的包,里面编写celery的配置文件,celery任务的文件,使用celery worker -A proj -l info -P eventlet命令去启动消费者文件,去监听消息队列。
    • 2、在另外一个目录下创建生产者文件(主函数),生产者文件作用是将celery任务放到消息队列里面,然后执行文件监听到消息队列有celery任务的话则就会去执行消息队列中的celery任务
  • 多目录结构下定时任务的执行方式不同,不是通过生产者去将celery任务插入到消息队列中,而是通过使用命令celery beat -A proj去将celery任务定时的插入到消息队列中,生产者不参与,定时任务或异步任务的调度代码都会在消费者的配置文件中去编写

  • 多目录结构中的celery.py文件:

  • import crontab
    import celery
    from datetime import  timedelta, datetime
    backend = 'redis://127.0.0.1:6379/1'
    broker = 'redis://127.0.0.1:6379/2'
    
    cel = celery.Celery('celery_test',
                        backend=backend,
                        broker=broker,
                        include=['celery_tasks.task01','celery_tasks.task02'])
    
    # 时区
    cel.conf.timezone = 'Asia/Shanghai'
    # 是否使用UTC
    cel.conf.enable_utc = False
    
    # 使用celery beat -A proj 命令执行定义好的定时任务
    cel.conf.beat_schedule = {
        # 名字随意命名
        'add-every-5-seconds':{
            # 执行task01下的send_email函数
            'task': 'celery_tasks.task01.send_mail',
            # 每隔6秒执行一次
            # 'schedule': 6.0,
            # 每隔6分钟
            # 'schedule': crontab(minute="*/6"),
            'schedule':timedelta(seconds=6),
            'args': ('Bob',)
        },
        'add-every-special-date':{
            'task':'celery_tasks.task02.send_msg',
            # 'schedule':crontab(minute=42,hours=8,day_of_month=11, month_of_year=11),
            'schedule':timedelta(seconds=7),
            'args':('Michael',)
        }
    
    }
    
  • 顺序(看个人):先开启监听,后将任务放入到消息队列,所以是先celery worker后celery beat

  • 注意点

    • 如果关闭了监听,beat命令还开着的话,还是会继续将celery任务不断的插入到broker中(这里使用redis),即使后面关闭了beat,其实redis中还是存放了celery任务。会导致下次worker开启监听的时候,会执行redis上一次beat遗留下来的celery任务,我们可以手动删除redis中的任务,可以通过pycharm去连接redis并删除对应的key

    • import redis
      
      r = redis.Redis(host='127.0.0.1', port=6379, db=2)
      
      # 删除beat历史遗留下来的celery任务
      r.delete('celery')
      
      # 查看还有没有历史遗留任务
      for i in r.lrange('celery',0,-1):
          print(i)
      
      

三、Django中使用celery

注意点

  • 1、celery不支持python3.7
  • 2、celery4.x已经不支持windows了,celery==3.1.25还是支持的
  • 3、常见错误:https://blog.csdn.net/cn_1937/article/details/91992075

概念介绍

  • Django需要引入celery包,django充当生产者,celery包充当消费者

  • django职责:

    • 只需要在views函数中去调用celery包封装好的函数
  • celery包职责:

    • 1、编写配置文件:指定消息队列和任务结果存储的数据库
    • 2、编写主函数:实例化celery对象并给对象添加一些属性,例如指定加载的配置、执行加载的celery任务有哪些、
    • 3、编写celery任务函数
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oDLoyLjJ-1596387123648)(C:\Users\dujun\AppData\Roaming\Typora\typora-user-images\image-20200803003440969.png)]

总体流程

  • 1、定义好celery包
  • 2、django中写好调用celery的代码
  • 3、消费者开启监听:使用命令行启动,建议切换目录到mycelery根目录下启动,celery -A mycelery.main worker --loglevel=info
  • 4、开启django项目
  • 5、去访问对应的url触发异步任务

文件对应的代码

  • 配置文件config.py:

    • broker_url = 'redis://127.0.0.1:6379/15'
      result_backend = 'redis://127.0.0.1:6379/14'
      
  • 主程序main.py

    • # 主程序
      import os
      from celery import Celery
      # 创建celery实例对象
      app = Celery("sms")
      
      # 把celery和django进行组合,识别和加载django的配置文件
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celeryPros.settings.dev')
      
      # 通过app对象加载配置
      app.config_from_object("mycelery.config")
      
      # 加载任务
      # 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
      # app.autodiscover_tasks(["任务1","任务2"])
      app.autodiscover_tasks(["mycelery.sms",])
      
      # 启动Celery的命令
      # 强烈建议切换目录到mycelery根目录下启动
      # celery -A mycelery.main worker --loglevel=info
      
  • 任务文件tasks.py

    • #  celery的任务必须写在名为tasks.py的文件中,因为主程序中加载celery任务的函数会自动识别名为tasks.py的文件
      from mycelery.main import app
      import time
      import logging
      
      log = logging.getLogger('django')
      
      @app.task
      def send_sms(name):    # name表示设置任务的名称,如果不填写,则默认为函数名
          print('邮件内容:%s'%name)
          time.sleep(5)
          return 'send_sms:ok'
      
      @app.task
      def send_sms2(name):
          print('邮件2内容:%s'%name)
          time.sleep(5)
          return 'send_sms2:ok'
      
  • Django视图调用views.py

    • from django.shortcuts import render, HttpResponse
      from mycelery.sms.tasks import send_sms, send_sms2
      # Create your views here.
      from datetime import datetime, timedelta
      
      def test(request):
          # 异步方式
          result = send_sms.delay('hi Bob')
          result2 = send_sms2.delay('hi Mechael')
          print(result.id)
          print(result2.id)
          # send_sms.delay() 如果调用的任务函数没有参数,则不需要填写任何内容
      
          ################################# 定时任务
      
          # ctime = datetime.now()
          # # 默认用utc时间
          # utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
          # time_delay = timedelta(seconds=10)
          # task_time = utc_ctime + time_delay
          # result = send_sms.apply_async(["hi boy", ], eta=task_time)
          # print(result.id)
          return HttpResponse('OK')
      

你可能感兴趣的:(celery,Django,python,分布式,celery)