2021-06-04

 celery使用



# Celery 介绍和使用

思考:

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

结论:

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

### 介绍

> celery 是一个简单、灵活且可靠、处理大量消息的分布式系统,可以在一台或者多台机器上运行.
>
> 特点:
>
> 单个 Celery 进程每分钟可处理数以百万计的任务.
>
> 通过消息进行通信,使用`消息队列( 中间人或broker )`在`生产者`和`消费者`之间进行协调。

### 安装

```bash
# 安装到虚拟环境, 你懂得~
pip install Celery
```

> [Celery 官方文档](http://docs.celeryproject.org/en/latest/index.html)

### 创建实例并且配置

##### 1. 定义 Celery 包

> 在外层 meiduo_mall 增加一个包, 名字叫做: celery_tasks:

##### 2. 创建 Celery 实例

> 在 celery_tasks 包中添加 main.py 文件:

> 在这个文件中添加如下的代码:

```python
# 从你刚刚下载的包中导入 Celery 类
from celery import Celery


# 利用导入的 Celery 创建对象
celery_app = Celery('meiduo')
```

> 注意: 这里的 'meiduo' 字符串可以使用别的字符串替换.

##### 3. 加载 Celery 配置

> 在 celery_tasks 包中再添加一个 config.py 文件:

> 在这个文件中指定一下 消息队列( 中间人 )的位置:

```python
# 如果使用 redis 作为中间人
# 需要这样配置:
broker_url='redis://127.0.0.1:6379/3'



# 如果使用别的作为中间人, 例如使用 rabbitmq
# 则 rabbitmq 配置如下:
broker_url= 'amqp://用户名:密码@ip地址:5672'

# 例如: 
# meihao: 在rabbitq中创建的用户名, 注意: 远端链接时不能使用guest账户.
# 123456: 在rabbitq中用户名对应的密码
# ip部分: 指的是当前rabbitq所在的电脑ip
# 5672: 是规定的端口号
broker_url = 'amqp://meihao:123456@172.16.238.128:5672'
```

> 说明:
>
> 上面的配置, 选择一个即可, 我们这里使用的是 redis
>
> 如果以后进入公司, 使用的不是 redis 作为存储的中间人.
>
> 则可以像我下面设置的那样, 设置别的工具作为中间人, 例如: rabbitmq

配置好中间人后, 就可以使用了吗? →_→ 想多了….

我们还需要把 config 添加到刚刚创建的对象中:

> 在 celery_tasks.main.py 中, 额外增加如下代码:

```python
from celery import Celery

celery_app = Celery('meiduo')

# 将刚刚的 config 配置给 celery
# 里面的参数为我们创建的 config 配置文件:
celery_app.config_from_object('celery_tasks.config')
```

### 定义任务

##### 注册任务:

> 我们需要在 celery_tasks 包下, 再创建一个包, 名字随意
>
> 例如, 我这里为 sms.
>
> 创建好后, 需要在里面添加一个 tasks.py 文件.
>
> 注意, 这里的 tasks 名字是规定死的.
>
> 然后需要在 celery_tasks.main.py 报备刚刚创建的文件:

```python
from celery import Celery

celery_app = Celery('meiduo')

celery_app.config_from_object('celery_tasks.config')

# 让 celery_app 自动捕获目标地址下的任务: 
# 就是自动捕获 tasks
celery_app.autodiscover_tasks(['celery_tasks.sms'])
```

##### 实现任务:

> 已经报备之后, 我们就可以在 tasks.py 中添加具体的内容了
>
> 在 celery_tasks.sms.tasks.py 文件中添加如下代码:

```python
from celery_tasks.main import celery_app

@celery_app.task(name='ccp_send_sms_code')
def ccp_send_sms_code(mobile, sms_code):
    '''该函数就是一个任务, 用于发送短信'''
    result = CCP().send_template_sms(mobile, 
                                     [sms_code, 5], 
                                     1)
    return result
```

> 注意:
>
> 我们添加完该函数后, 会发现 CCP() 是调用我们项目中的 libs.yuntongxun 中的文件, 但是, 真实的开发环境有可能会把 celery_tasks 单独拿到某一个电脑上独立执行, 会和美多商城项目分开. 所以那时就找不到 libs 了.
>
> 我们可以把 yuntongxun 复制一份, 放到 celery_tasks 下面, 拿走的时候, 直接调用走就可以.
>
> 如下图所示:

##### 调用任务

> 我们创建了一个任务, 现在就可以调用该任务了.
>
> 没有使用这个任务之前, 我们发送短信调用的代码写在了
>
> verifications.views.py 中. 所以我们现在这个任务的调用还是
>
> 在那个位置, 用该任务替换以前发送短信的函数:

```python
from celery_tasks.sms.tasks import ccp_send_sms_code

# 原来的写法:
# CCP().send_template_sms(mobile, [sms_code, 5], 1)

# 改为现在的写法, 注意: 这里的函数,调用的时候需要加: .delay()
ccp_send_sms_code.delay(mobile, sms_code)
```

则, 我们现在的发送短信接口变为了:

```python
from celery_tasks.sms.tasks import ccp_send_sms_code

class SMSCodeView(View):
    def get(self, reqeust, mobile):
        redis_conn = get_redis_connection('verify_code')
        send_flag = redis_conn.get('send_flag_%s' % mobile)
        if send_flag:
            return JsonResponse({'code': 400,
                                 'errmsg': '发送短信过于频繁'})

        image_code_client = reqeust.GET.get('image_code')
        uuid = reqeust.GET.get('image_code_id')

        if not all([image_code_client, uuid]):
            return JsonResponse({'code': 400,
                                 'errmsg': '缺少必传参数'})

        image_code_server = redis_conn.get('img_%s' % uuid)
        if image_code_server is None:
            return JsonResponse({'code': 400,
                                 'errmsg': '图形验证码失效'})

        try:
            redis_conn.delete('img_%s' % uuid)
        except Exception as e:
            logger.error(e)

        image_code_server = image_code_server.decode()
        if image_code_client.lower() != image_code_server.lower():
            return http.JsonResponse({'code': 400,
                                      'errmsg': '输入图形验证码有误'})

        sms_code = '%06d' % random.randint(0, 999999)
        logger.info(sms_code)

        pl = redis_conn.pipeline()
        pl.setex('sms_%s' % mobile, 300, sms_code)
        pl.setex('send_flag_%s' % mobile, 60, 1)
        pl.execute()


        # 原来的写法:
        # CCP().send_template_sms(mobile, [sms_code, 5], 1)

        # 改为现在的写法, 注意: 这里的函数,调用的时候需要加: .delay()
        ccp_send_sms_code.delay(mobile, sms_code)

        return JsonResponse({'code': 0,
                             'errmsg': '发送短信成功'})
```

至此, 我们终于把生产者的事情做完了. 也可以发送任务了.

那要不要吃个甜甜圈? 休息一下? ←_←

想的挺美.....

接下来, 我们就需要启动消费者了.

注意:

说是消费者, 其实就是个打工仔. 所以我们需要启动个 worker

### 启动消费者( celery充当 )

```bash
# 想要启动 celery 服务, 调用下面的命令行: 
cd ~/projects/meiduo_project/meiduo_mall

celery -A celery_tasks.main worker -l info
```

> 参数说明:
>
> celery : 调用 celery 命令
>
> -A : 作用是指定要启动的文件, 这个参数后面的文件会被执行.
>
> worker : 启动的对象是 worker, 工人, 干活的人
>
> -l : 指日志打印等级, 一般日志都是通知形式的, 即: info 级别

### 补充 celery worker 的工作模式

默认是进程池方式:

> 进程数以当前机器的CPU核数为参考,每个CPU开四个进程

如何自己指定进程数:

```python
celery worker -A proj --concurrency=4
```

如何改变进程池方式为协程方式:

```python
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
```

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值