Django实现Celery

异步方案Celery机制

  1. 场景

    1. 发送短信验证码

      我们的代码是自上而下同步执行的,发送短信是耗时的操作。如果短信被阻塞住,用户响应将会延迟,响应延迟会造成用户界面的倒计时延迟。异步发送短信,发送短信和响应分开执行,将发送短信从主业务中解耦出来。

    2. 发送邮箱链接激活邮箱

      发送邮箱验证邮件是耗时的操作,所以需要异步发送邮件,使用Celery实现异步任务。

    3. 并发下单问题

      在多个用户同时发起对同一个商品的下单请求时,先查询商品库存,再修改商品库存,会出现资源竞争问题,导致库存的最终结果出现异常,将下单的逻辑放到任务队列中(如celery),将并行转为串行,所有人排队下单。比如开启只有一个进程的Celery,一个订单一个订单的处理。

    4. 总结:只要有可能发生耗时延迟的情况都可以考虑异步方案celery。

  2. 生产者消费者设计模式介绍
    • 为了将发送短信从主业务中解耦出来,我们引入生产者消费者设计模式

    • 它是最常用的解耦方式之一,寻找**中间人(broker)**搭桥,保证两个业务没有直接关联
      Django实现Celery_第1张图片
      总结:

    • 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。

    • 由服务器生成发送短信消息,缓存到消息队列中,消费者读取消息队列中的发送短信消息并执行。

  3. Celery介绍和使用
    1. 场景

      • 消费者取到消息之后,要消费掉(执行任务),需要我们去实现。
      • 任务可能出现高并发的情况,需要补充多任务的方式执行。
      • 耗时任务很多种,每种耗时任务编写的生产者和消费者代码有重复。
      • 取到的消息什么时候执行,以什么样的方式执行。
    2. 解决办法

      • 实际开发中,我们可以借助成熟的工具Celery来完成。
      • 有了Celery,我们在使用生产者消费者模式时,只需要关注任务本身,极大的简化了程序员的开发流程。
    3. Celery介绍

      • 一个简单、灵活且可靠、处理大量消息的分布式系统,可以在一台或者多台机器上运行。
      • 单个 Celery 进程每分钟可处理数以百万计的任务。
      • 通过消息进行通信,使用消息队列(broker)在客户端和消费者之间进行协调。
    4. 在虚拟环境中安装Celery

      pip install -U Celery
      
    5. 创建Celery实例并加载配置

      步骤:

      1. 定义Celery包:

        在项目工程目录下创建python包celery_tasks

        在celery_tasks包下创建main.py入口文件

        在celery_tasks包下创建config.py配置文件

      2. 加载Celery配置

        在config.py配置文件中指定消息队列的位置

        # 指定消息队列的位置
        broker_url = "redis://127.0.0.1/10"
        
      3. 创建Celery实例:

        在main.py入口文件中创建实例

        # celery启动文件
        from celery import Celery
        
        # 创建celery实例,名称可以不传
        celery_app = Celery('名称')
        
        # 加载celery配置
        celery_app.config_from_object('celery_tasks.config')
        
    6. 定义Celery任务,例:发送短信任务

      步骤:先在Celery包创建任务包,在任务包中创建tasks.py(名字固定)文件

      1. 在tasks.py中定义任务

        from celery import Task
        from celery_tasks.sms.yuntongxun.ccp_sms import CCP
        from celery_tasks.sms import constants
        from celery_tasks.main import celery_app
        
        
        # 判断是否执行任务成功
        class MyTask(Task):
            def on_success(self, retval, task_id, args, kwargs):
                # 注意自己定义的任务
                print('调用异步任务完成')
                print('retval', retval)
                print('task_id', task_id)
                return super(MyTask, self).on_success(retval, task_id, args, kwargs)
        
            def on_failure(self, exc, task_id, args, kwargs, einfo):
                print('调用异步任务失败')
                print('retval', exc)
                print('task_id', task_id)
                return super(MyTask, self).on_failure(exc, task_id, args, kwargs)
        
        
        # 使用装饰器装饰异步任务,保证celery识别任务
        @celery_app.task(name='send_sms_code')
        def send_sms_code(mobile, sms_code):
            """
            发送短信验证码的异步任务
            :param mobile: 手机号
            :param sms_code: 短信验证码
            :return: 成功:0,失败:-1
            """
            # 发送短信验证码
            send_ret = CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
            return send_ret
        
        
      2. 注册任务

        # celery启动文件
        from celery import Celery
        
        
        # 创建celery实例
        celery_app = Celery('名称')
        
        # 加载celery配置
        celery_app.config_from_object('celery_tasks.config')
        
        # 自动注册celery任务
        celery_app.autodiscover_tasks(['celery_tasks.sms'])
        
        # 注册多个任务只要在后面添加任务即可
        # celery_app.autodiscover_tasks(['celery_tasks.sms', 'celery_tasks.email'])
        
    7. 启动Celery服务

      # 进入项目工程所在目录
      celery -A celery_tasks.main worker -l info
      
      # -A指对应的应用程序, 其参数是项目中 Celery实例的位置。
      # worker指这里要启动的worker。
      # -l指日志等级,比如info等级。
      
    8. 在要发送短信的位置调用发短信任务

      # Celery异步发送短信验证码
      ccp_send_sms_code.delay(mobile, sms_code)
      
  4. celery错误重试机制
    # bind:保证task对象会作为第一个参数自动传入
    # name:异步任务别名
    # retry_backoff:异常自动重试的时间间隔 第n次(retry_backoff×2^(n-1))s
    # max_retries:异常自动重试次数的上限
    import logging
    
    from django.conf import settings
    from django.core.mail import send_mail
    from celery_tasks.main import celery_app
    
    logger = logging.getLogger('django')
    
    @celery_app.task(bind=True, name='send_verify_email', retry_backoff=3)
    def send_verify_email(self, to_email, verify_url):
        """
        发送验证邮箱邮件
        self:当前任务对象
        :param to_email: 收件人邮箱
        :param verify_url: 验证链接
        :return: None
        """
        subject = "美多商城邮箱验证"
        html_message = '

    尊敬的用户您好!

    '
    \ '

    感谢您使用美多商城。

    '
    \ '

    您的邮箱为:%s 。请点击此链接激活您的邮箱:

    '
    \ '

    %s

    '
    % (to_email, verify_url, verify_url) try: send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message) except Exception as e: logger.error(e) # 有异常自动重试三次 raise self.retry(exc=e, max_retries=3)
  5. celery worker的工作模式(增加celery的并发量)
    • 默认是进程池方式,进程数以当前机器的CPU核数为参考,每个CPU开四个进程。

    • 如何自己指定进程数:celery worker -A proj --concurrency=4 or celery worker -A proj --c 20

    • 如何改变进程池方式为协程方式:celery worker -A proj --concurrency=1000 -P eventlet -c 1000

      # 安装eventlet模块
      $ pip install eventlet
      
      # 启用 Eventlet 池
      $ celery -A celery_tasks.main worker -l info -P eventlet -c 1000
      

你可能感兴趣的:(Django框架,异步方案Celery机制,生产者消费者模式,Celery错误机制重试,Celery,worker工作模式)