Celery

1.1 Celery介绍

# Celery
1.celery 是一个用于实时处理和任务调度的分布式任务队列 同时提供操作和维护分布式系统所需的工具
2.celery 封装了操作常见任务队列的各种操作 能够快速进行任务队列的使用与管理

# Celery特征
1.celery 可选高并发执行任务模式:多进程 Eventlet Gevent
2.celery 提供错误处理机制
3.celery 支持多种消息代理(rabbitmq)和存储后端(redis)
4.celery 使用管理后台和命令对任务进行维护管理
5.celery 可查看定时任务的执行情况  

# 生产者与消费者数据传输需要将数据进行序列化&&反序列化操作
1.通过特定模块实现-msgpack(二进制的类json序列化 推荐) pickle json yaml
2.安装celery工具集成包 pip install "celery[librabbitmq, redis, msgpack, gevent]"


# 小结:Celery核心组件: celery客户端(发布任务) + Celery worker(运行任务) + 消息代理(客户端与worker通信方式)

celery架构图


1.2 celery安装

# 默认python 环境
pip install -U Celery -i  https://mirrors.aliyun.com/pypi/simple/
或
pip install "celery[librabbitmq, redis, msgpack, gevent]" -i https://mirrors.aliyun.com/pypi/simple/ 


1.3 celery 使用

###----task.py 发布任务
from celery import Celery


# 初始化默认celery
app = Celery('task',  # 模块名
             broker='amqp://guest@localhost',  # 消息代理rqbbitmq(本地已安装rabbitmq或docker容器单开)
             backend="redis://localhost:6379//0")  # 结果存储redis/0(本地已安装redis或docker容器单开)

# 发布的任务
@app.task
def cal(x, y):
    return x + y

#----worker.py 执行任务(可远程可本地)
from task import cal

###----worker.py 执行任务
ret = cal.delay(15, 10)  # 携带参数较少
print('计算结果:', ret.get(timeout=1))

# cal.apply_async(args=(15, 10), kwargs={'a':1, 'b': 2})  # 可携带更多参数


###---terminal终端执行命令
celery -A task worker -l info (window10 加上参数-P threads)
-A:指定celery实例位置
-l:指定worker输出日志等级
-Q:可指定队列
    
    
### 小结:
1.app.task装饰cal函数成一个Task实例;cal.delay函数将task实例序列化后 发送到rabbitmq;
2.task.py执行这个过程中创建了一个Exchagne(direct):celery交换机;创建了一个queue(routing_key=celery):celery队列;
3.保证app.task装饰器在最外层

1.4 celery 配置

# 修改celery默认配置
1.app.conf.task_serializer = 'json' 或
app.conf.update(
    task_serializer='json',  # 任务序列化格式
    accept_content=['json'],  # Ignore other content
    result_serializer='json',  # 存储结果序列化格式
    timezone='Europe/Oslo',  # 时区
    enable_utc=True,
)


2.通过配置文件初始化celery
#----celery_config.py(celery4+版本 可大小写)
BROKER_URL = "redis://localhost:6379/1"  # broker(消息队列)地址(celery:4+)
CELERY_RESULT_BACKEND = "redis://localhost:6379/0"  # 结果存储地址
CELERY_TASK_SERIALIZER = "json"  # 任务序列化方式
CELERY_RESULT_SERIALIZER = "json"  # 任务执行结果序列化方式
CELERY_TASK_RESULT_EXPIRES = 60  # 任务过期时间(second)
CELERY_ACCEPT_CONTENT = ['json', ]  # 指定任务接受的内容类型-序列化

# 测试配置文件是否格式正确
python -m celery_config

# 加载配置文件
app = Celery(__name__, include=['task'])
app.config_from_object('celery_config')



# 配置文件详情:https://docs.celeryproject.org/en/latest/userguide/configuration.html#configuration
# 常用配置文件信息
# 指定消息代理
BROKER_URL = 'amqp://username:passwd@host:port/虚拟主机名'
# 指定结果的接受地址
CELERY_RESULT_BACKEND = 'redis://username:passwd@host:port/db'
# 指定任务序列化方式
CELERY_TASK_SERIALIZER = 'msgpack' 
# 指定结果序列化方式
CELERY_RESULT_SERIALIZER = 'msgpack'
# 任务过期时间,celery任务执行结果的超时时间(s)
CELERY_TASK_RESULT_EXPIRES = 60 
# 指定任务接受的序列化类型.
CELERY_ACCEPT_CONTENT = ["msgpack"]   
# 任务发送完成是否需要确认,这一项对性能有一点影响     
CELERY_ACKS_LATE = True  
# 压缩方案选择,可以是zlib, bzip2,默认是发送没有压缩的数据
CELERY_MESSAGE_COMPRESSION = 'zlib' 
# 规定完成任务的时间
CELERYD_TASK_TIME_LIMIT = 5  # 在5s内完成任务,否则执行该任务的worker将被杀死,任务移交给父进程
# celery worker的并发数,默认是服务器的内核数目,也是命令行-c参数指定的数目
CELERYD_CONCURRENCY = 4 
# celery worker 每次去rabbitmq预取任务的数量
CELERYD_PREFETCH_MULTIPLIER = 4 
# 每个worker执行了多少任务就会死掉,默认是无限的
CELERYD_MAX_TASKS_PER_CHILD = 40 
# 设置默认的队列名称,如果一个消息不符合其他的队列就会放在默认队列里面,如果什么都不设置的话,数据都会发送到默认的队列中
CELERY_DEFAULT_QUEUE = "default" 
# 设置详细的队列
CELERY_QUEUES = {
    "default": { # 指定的默认队列
        "exchange": "default",
        "exchange_type": "direct",
        "routing_key": "default"
    },
    "topicqueue": { # 通配符routing_key匹配队列 凡是topic开头的routing key都会被放到这个队列
        "routing_key": "topic.#",
        "exchange": "topic_exchange",
        "exchange_type": "topic",
    },
    "task_eeg": { # 设置广播类型交换机
        "exchange": "tasks",
        "exchange_type": "fanout",
        "binding_key": "tasks",
    },
}

1.5 celery 注意点

# 指定任务使用特定队列
from kombu import Queue

app.conf.task_default_queue = 'default'
app.conf.task_queues = (
    Queue('default',    routing_key='task.#'),
    Queue('web', routing_key='web.#'),
)
app.conf.task_default_exchange = 'topic_task'
app.conf.task_default_exchange_type = 'topic'
app.conf.task_default_routing_key = 'task.default'

----terminal
celery -A celery_app worker -Q web


2. 实例操作

2.1 短信异步服务

# 准备工作
# 1.redis服务开启 
# 2.rabbitmq服务开启(docker run --rm -it --name mq -p 5672:5672 -p 15672:15672 rabbitmq:management)
# 3.阿里云短信服务 模板 签名 ak ak-sercet获取
# 4.pip 模块 
#----requirements.txt
aliyun-python-sdk-core-v3==2.13.11
amqp==2.6.1
billiard==3.6.3.0
celery==4.4.7
importlib-metadata==1.7.0
jmespath==0.10.0
kombu==4.6.11
pika==1.1.0
pytz==2020.1
redis==3.5.3
vine==1.3.0
zipp==3.1.0
#----

#----task2.py
from celery import Celery
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest

# 创建celery实例
app = Celery('task2',
    		broker="amqp://guest@localhost//",
             backend="redis://localhost:6379/1")


@app.task
def send(send_phone, code):
    client = AcsClient('accessKeyId', 'accessSecret', 'cn-hangzhou')
    
    request = CommonRequest()
    request.set_accept_format('json')
    request.set_domain('dysmsapi.aliyuncs.com')
    request.set_method('POST')
    request.set_protocol_type('https')  # https | http
    request.set_version('2017-05-25')
    request.set_action_name('SendSms')
    
    request.add_query_param('RegionId', "cn-hangzhou")
    request.add_query_param('PhoneNumbers', send_phone)
    request.add_query_param('SignName', "签名")
    request.add_query_param('TemplateCode', "SMS_模板")
    request.add_query_param('TemplateParam', "{\'code\':\'%s\'}" % code)
    
    response = client.do_action(request)
    response = str(response, encoding="utf-8")
    return response


# if __name__ == '__main__':
#     res = send("手机号", "短信验证码")
#     print(res)



#----worker2.py
#coding=utf-8
from celery import Celery

# 远程需要配置celery信息
app = Celery("worker2",
    		broker="amqp://guest@localhost//",
             backend="redis://localhost:6379/1")

app.send_task('task2.send', args=["手机号", "短信验证码"])


# win10终端
#$celery -A task2 worker -l info -P threads


短信服务


2.2 邮件异步服务

#----task.py
#encoding=utf8
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr

from celery import Celery


# 邮件配置host port
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
# 发送方的邮箱
EMAIL_HOST_USER = 'yyy@163.com'

# 163邮箱授权密码
EMAIL_HOST_PASSWORD = '授权密码'

# 发件人头信息
EMAIL_FROM = '测试邮件发送<yyy@163.com>'

app = Celery(
    'task',
    #broker='amqp://guest@localhost//',
    broker='redis://localhost:6379/1',
    backend='redis://localhost:6379/2'
)


# 头信息中文处理
def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))


# 任务
@app.task
def send(receivers, content, subject, mail_from=None):
    # 编辑邮件信息
    message = MIMEText(content)  # 邮件正文内容
    message['from'] = _format_addr(EMAIL_FROM if not mail_from else mail_from)  # 发件人标题
    message['to'] = ','.join(receivers)  # 接收方
    message['subject'] = Header(subject, 'utf-8')  # 主题
    
    # 第三方登录163 发送邮件
    smtp_obj = smtplib.SMTP()
    smtp_obj.connect(EMAIL_HOST, EMAIL_PORT)
    smtp_obj.set_debuglevel(1)
    smtp_obj.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
    smtp_obj.sendmail(EMAIL_FROM, receivers, message.as_string())
    smtp_obj.quit()


if __name__ == '__main__':
    send(['xxx@qq.com'], '你好,邮件发送测试成功', '测试邮件发送')
    

#----worker.py
#coding=utf8
from celery import Celery

app = Celery(
    'worker',
    #broker='amqp://guest@localhost//',
    broker='redis://localhost:6379/1',
    backend='redis://localhost:6379/2'
)

# 执行任务
app.send_task('task.send', args=[['xxx@qq.com'], '你好,邮件发送测试成功', '测试邮件发送'])


# terminal
# celery -A task worker -l info -P threads

发送邮件服务



5. 帮助文档

celery: https://docs.celeryproject.org/en/latest/getting-started/introduction.html

简书:https://www.jianshu.com/p/9be4d8d30d8e

posted @ 2020-10-26 20:33  爱编程_喵  阅读(175)  评论(0)    收藏  举报
jQuery火箭图标返回顶部代码

jQuery火箭图标返回顶部代码

滚动滑动条后,查看右下角查看效果。很炫哦!!

适用浏览器:IE8、360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗.