背景
在同步编程模型中,当一个任务执行时,程序会等待该任务完成后再执行下一个任务。这种方式在处理一些耗时的操作时会导致程序阻塞,从而降低了程序的性能和响应能力。
而异步编程模型则允许程序在执行一个任务时,同时处理其他任务或等待其他任务的完成。在异步模型中,任务可以通过非阻塞的方式执行,即任务执行过程中不会阻塞程序的其他部分。这样可以充分利用计算机的资源,提高程序的并发性和响应性能。
说明
下面为示例代码,具体项目请根据需求进行配置。
环境配置
- 安装依赖库
django-redis==5.4.0
celery==5.1.2
- 在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}
}
}
}
- 在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
- 在项目下新建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']
异步任务
- 修饰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')
- 在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"})
- 运行程序
- 运行celery,可以看到task1和task2已经在 '[tasks]' 列表中了。
celery -A HelloWorld worker -I info
- 运行Django服务
python manage.py runserver 0.0.0.0:8000
发送请求的时候服务端立刻返回响应,celery在后台分别等待10s和20s之后完成任务的执行。
定时任务
- 在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
- 在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")
- 执行定时任务
- 只执行定时任务
celery -A HelloWorld beat -l INFO
输出如下:
- 执行定时任务与异步任务
所有任务都注册到了 [tasks] 中。
celery -A HelloWorld worker -B -l INFO
输出如下:
总结
使用Celery和Redis可以将任务逻辑与主应用程序解耦,实现模块化的开发。通过增加工作进程或工作节点,可以实现任务的水平扩展,以适应不断增长的任务负载。而且任务可以独立于应用程序进行开发、测试和部署,从而提高代码的可维护性和可扩展性。