celery和django结合处理异步任务

问题

开发中涉及到一些执行时间长的任务,造成用户等待时间过长影响体验,并发量大的话任务执行也不稳定。

方案

使用celery分布式任务调度框架,生产者将任务先缓存至消息中间件,消费者从中间件获取任务执行;

celery和django结合处理异步任务_第1张图片

优势:

            1. 异步提升性能,任务生成和任务执行逻辑分离,降低耦合性,增强用户体验
            2. 数据缓冲,任务上报的速率由用户决定,服务端不可控,此模式可以缓存任务执行速率,达到流量削峰目的,避免引发系统崩溃
            3. 易于扩展,在任务量大时,通过增加任务处理Worker来扩展,提高处理速率

劣势:系统复杂度增加(可以接受)

实施

工具

broker:rabbitmq

backend:redis

操作流程

生产者配置:

django web站点执行任务发布,settings.py配置如下:

from celery import Celery
from kombu import Exchange
# celery配置
APP = Celery()

REDIS_URI = "redis://:%s@%s:%s/%s" % (
    CONFIG_INFO.get("REDIS_PASSWORD"),
    CONFIG_INFO.get("REDIS_HOST"),
    CONFIG_INFO.get("REDIS_PORT"),
    CONFIG_INFO.get("REDIS_DB")
)
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': REDIS_URI,
        'OPTIONS': {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            'MAX_ENTRIES': 1000,
            'CULL_FREQUENCY': 3,
        }
    }
}

APP.conf.update(
    task_serializer='msgpack',  # 任务序列化和反序列化使用msgpack方案
    # result_serializer='json',   # 读取任务结果, 一般性能要求不高,所以使用可读性更好的JSON
    timezone='Asia/Shanghai',  # 指定时区,不指定默认为 'UTC'
    broker_url='amqp://{}:{}@{}:{}'.format(CONFIG_INFO.get('MQ_NAME'), CONFIG_INFO.get('MQ_PASSWORD'),
                                           CONFIG_INFO.get('MQ_URL'), CONFIG_INFO.get('MQ_PORT')),  # broker地址
    result_backend=REDIS_URI  # 存储任务结果
)

# 定义交换机,和消费者保持一致
Exchange('classify', type='topic')

生产者发布任务:

def task_classify(args=None):
    """题目归类任务"""
    task_id = ''  # 任务ID
    args = args or {}
    result = settings.APP.send_task('tasks.classify.classify', (args,), exchange='classify',
                                    routing_key="tasks.classify.classify", eta=None)
    task_id = result.id
    log.info(result)
    return task_id

任务发布后返回任务id,供客户端获取任务执行结果时用

生产者获取任务结果:

def get_task(request):
    """获取指定ID的task"""
    task_id = request.POST.get('task_id', '')
    result = AsyncResult(task_id)
    data = {
        'task_id': result.id,
        'status': result.status,
        'result': result.result
    }
    return ajax_ok(data)

消费者配置:

消费者使用python脚本,通过supervisord部署管理,使用flower查看执行结果

任务执行模块:

celery和django结合处理异步任务_第2张图片

关键配置:

# broker地址
broker_url = 'amqp://{}:{}@{}:{}'.format(CONFIG_INFO.get('MQ_NAME'),
                                         CONFIG_INFO.get('MQ_PASSWORD'),
                                         CONFIG_INFO.get('MQ_URL'),
                                         CONFIG_INFO.get('MQ_PORT'))

# 存储任务结果地址
result_backend = f"redis://:{CONFIG_INFO.get('REDIS_PASSWORD')}@{CONFIG_INFO.get('REDIS_HOST')}:" \
                 f"{CONFIG_INFO.get('REDIS_PORT')}/{CONFIG_INFO.get('REDIS_DB')}"

# 创建交换机
default_exchange = Exchange('default', type='topic')
classify_exchange = Exchange('classify', type='topic')  # 归类

# 指定队列
task_queues = (
    Queue('default', exchange=default_exchange, routing_key='*.default.*', delivery_mode=1),
    # 路由键包含“defaul”的消息都进default队列
    Queue('classify', exchange=classify_exchange, routing_key='*.classify.*', delivery_mode=1),
    # 路由键包含“classify”的消息都进classify队列
)

使用supervisord启用消费者和flower服务

[program:celery.worker.zykxcopy]
;celery directory
directory=/code
;运行目录下执行命令,启动worker
command=celery worker -A tasks.classify -Q classify -P gevent -c 8 -l info -n classify_worker

[program:flower]
directory=/code
command=celery flower -A tasks.flower --conf=/code/configs/flowerconfig.py --basic_auth=****:****
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

你可能感兴趣的:(服务器,软件架构,django,rabbitmq,redis)