背景

在同步编程模型中,当一个任务执行时,程序会等待该任务完成后再执行下一个任务。这种方式在处理一些耗时的操作时会导致程序阻塞,从而降低了程序的性能和响应能力。

而异步编程模型则允许程序在执行一个任务时,同时处理其他任务或等待其他任务的完成。在异步模型中,任务可以通过非阻塞的方式执行,即任务执行过程中不会阻塞程序的其他部分。这样可以充分利用计算机的资源,提高程序的并发性和响应性能。

说明

下面为示例代码,具体项目请根据需求进行配置。

环境配置

  1. 安装依赖库
django-redis==5.4.0
celery==5.1.2
  1. 在settings.py中配置Redis
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'CONNECTION_POOL_KWARGS': {'max_connections': 100}
        }
    }
}
  1. 在settings.py中配置celery
CELERY_BROKER_URL = 'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}'
CELERY_RESULT_BACKEND = 'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}'
CELERY_RESULT_SERIALIZER = 'json'
CELERYD_CONCURRENCY = 1
CELERYD_MAX_TASKS_PER_CHILD = 1
CELERY_ENABLE_UTC = True
  1. 在项目下新建celery.py
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'HelloWorld.settings')

app = Celery('HelloWorld')

app.config_from_object('django.conf:settings', namespace='CELERY')

app.autodiscover_tasks()

__all__ = ['app']

异步任务

  1. 修饰tasks.py中的任务
from HelloWorld.celery import app
from celery.utils.log import get_task_logger
import time

logger = get_task_logger(__name__)

@app.task
def task1():
    logger.info('task1 start')
    time.sleep(10)
    logger.info('task1 finish')
    
@app.task
def task2():
    logger.info('task2 start')
    time.sleep(20)
    logger.info('task2 finish')
  1. 在views.py中调用异步任务
from .tasks import task1, task2
def celery(request):
    try:
        _ = task1.delay()
        _ = task2.delay()
        return JsonResponse({"Message": "success"})
    except Exception as e:
        return JsonResponse({"Message": "fail"})
  1. 运行程序
  • 运行celery,可以看到task1和task2已经在 '[tasks]' 列表中了。
celery -A HelloWorld worker -I info
  • 运行Django服务
python manage.py runserver 0.0.0.0:8000

发送请求的时候服务端立刻返回响应,celery在后台分别等待10s和20s之后完成任务的执行。

Django使用Celery+Redis执行异步和定时任务_celery

定时任务

  1. 在settings.py中添加定时任务配置
  • 'celery.tasks.task*' 为任务名称,可自行修改;
  • 必须要先导入任务模块;
  • 可以用 schedule、timedelta 等配置定时任务执行时间间隔;
  • 可以用 crontab 等配置任务执行时间点;
from celery.schedules import crontab
from datetime import timedelta

# 导入任务,否则会出现找不到任务的错误
CELERY_IMPORTS = ('HelloWorld.tasks',)

CELERY_BEAT_SCHEDULE = {
    'celery.tasks.task1': {
        'task': 'HelloWorld.tasks.task1',
        'schedule': 20
    },
    'celery.tasks.task2': {
        'task': 'HelloWorld.tasks.task2',
        'schedule': timedelta(seconds=40),
    },
    'celery.tasks.task3': {
        'task': 'HelloWorld.tasks.task3',
        'schedule': crontab(minute='*/1'),
    }
}

# 设置数据过期时间,从缓存中删除已经执行完成的定时任务
CELERY_RESULT_EXPIRES = 100
  1. 在tasks.py中添加任务内容
from celery.utils.log import get_task_logger
from HelloWorld.celery import app

logger = get_task_logger(__name__)

@app.task
def task1():
    logger.info("this is task1")

@app.task
def task2():
    logger.info("this is task2")

@app.task
def task3():
    logger.info("this is task3")
  1. 执行定时任务
  • 只执行定时任务
celery -A HelloWorld beat -l INFO

输出如下:

Django使用Celery+Redis执行异步和定时任务_celery_02

  • 执行定时任务与异步任务

所有任务都注册到了 [tasks] 中。

celery -A HelloWorld worker -B -l INFO

输出如下:

Django使用Celery+Redis执行异步和定时任务_redis_03

总结

使用Celery和Redis可以将任务逻辑与主应用程序解耦,实现模块化的开发。通过增加工作进程或工作节点,可以实现任务的水平扩展,以适应不断增长的任务负载。而且任务可以独立于应用程序进行开发、测试和部署,从而提高代码的可维护性和可扩展性。