主要介绍celery的配置,flask相关配置就默认大家都会了。
flask-object
|--apps
|--user
views.py
__init__.py
|--celery_task
__init__.py
asycn_task.py
celery.py
celeryconfig.py
check_task.py
scheduler_task.py
app.py
依赖包:
- celery==4.4.7
- eventlet==0.33.3
- Flask==2.1.3
- Flask-Caching==1.10.1
- Flask-Cors==3.0.10
- Flask-Migrate==2.7.0
- Flask-RESTful==0.3.9
- Flask-SocketIO==5.1.1
- Flask-SQLAlchemy==2.5.1
- PyMySQL==1.0.2
- redis==3.5.3
- SQLAlchemy==1.4.0
- Werkzeug==2.0.2
1、celery相关配置:celeryconfig.py
celery默认使用json作为序列化工具,要操作flask上传的文件,需要改为pickle
- from celery.schedules import crontab
- from datetime import timedelta
- '''
- 参数解析:
- accept_content:允许的内容类型/序列化程序的白名单,如果收到不在此列表中的消息,则该消息将被丢弃并出现错误,默认只为json;
- task_serializer:标识要使用的默认序列化方法的字符串,默认值为json;
- result_serializer:结果序列化格式,默认值为json;
- timezone:配置Celery以使用自定义时区;
- enable_utc:启用消息中的日期和时间,将转换为使用 UTC 时区,与timezone连用,当设置为 false 时,将使用系统本地时区。
- result_expires: 异步任务结果存活时长
- beat_schedule:设置定时任务
- '''
- #手动注册celery的异步任务:将所有celery异步任务所在的模块找到,写成字符串
- task_module = [
- 'celery_task.async_task', # 写任务模块导入路径,该模块主要写异步任务的方法
- 'celery_task.scheduler_task', # 写任务模块导入路径,该模块主要写定时任务的方法
- ]
-
- #celery的配置
- config = {
- "broker_url" :'redis://127.0.0.1:6379/0', #'redis://:123456@127.0.0.1:6379/1' 有密码时,123456是密码
- "result_backend" : 'redis://127.0.0.1:6379/1',
- "task_serializer" : 'pickle', #json格式支持的数据格式比较少,改为pickle
- "result_serializer" : 'pickle', #json格式支持的数据格式比较少,改为pickle
- "accept_content" : ['pickle'], #json格式支持的数据格式比较少,改为pickle
- "timezone" : 'Asia/Shanghai',
- "enable_utc" : False,
- "result_expires" : 1*60*60,
- "beat_schedule" : { #定时任务配置
- # 名字随意命名
- 'add-func-30-seconds': {
- # 执行add_task下的addy函数
- 'task': 'celery_task.scheduler_task.add_func', # 任务函数的导入路径,from celery_task.scheduler_task import add_func
- # 每10秒执行一次
- 'schedule': timedelta(seconds=30),
- # add函数传递的参数
- 'args': (10, 21)
- },
- # 名字随意起
- 'add-func-5-minutes': {
- 'task': 'celery_task.scheduler_task.add_func', # 任务函数的导入路径,from celery_task.scheduler_task import add_func
- # crontab不传的参数默认就是每的意思,比如这里是每年每月每日每天每小时的5分执行该任务
- 'schedule': crontab(minute='5'), # 之前时间点执行,每小时的第5分钟执行任务, 改成小时,分钟,秒 就是每天的哪个小时哪分钟哪秒钟执行
- 'args': (19, 22) # 定时任务需要的参数
- },
- # 缓存用户数据到cache中
- 'cache-user-func': {
- 'task': 'celery_task.scheduler_task.cache_user_func',
- # 导入任务函数:from celery_task.scheduler_task import cache_user_func
- 'schedule': timedelta(minutes=1), # 每1分钟执行一次,将用户消息缓存到cache中
- }
- }
- }
2、创建celery对象:celery.py
- from celery import Celery,Task
- from .celeryconfig import config,task_module
- import sys
- import os
- '1、把flask项目路径添加到系统环境变量中'
- project_path = os.path.dirname(os.path.dirname(__file__))
- sys.path.append(project_path)
-
- '''
- 2、创建celery应用对象
- 'task'可以任务是该celery对象名字,用于区分celery对象
- broker是指定消息中间件
- backend是指定任务结果存储位置
- include是手动指定异步任务所在的模块的位置
- '''
- #创建celery异步对象
- celery = Celery('task', broker=config.get('broker_url'), backend=config.get('result_backend'), include=task_module)
- #导入一些基本配置
- celery.conf.update(**config)
-
- '3、给celery所有任务添加flask的应用上下文,在celery异步任务中就可以调用flask中的对象了'
- class ContextTask(celery.Task):
- def __call__(self, *args, **kwargs):
- from apps import create_app
- app = create_app()
- with app.app_context():
- return self.run(*args, **kwargs)
- celery.Task = ContextTask
3、异步任务模块:async_task.py
- import time
- # 导入celery对象app
- from celery_task.celery import celery
- import os
- #导入发送邮件的模块
- from base.email_ import send_email
- from ext import cache
-
-
- '''
- 所有异步任务:
- 1、没有返回值的,@app.task(ignore_result=True)
- 2、有返回值的任务,@app.task 默认就是(ignore_result=False)
- '''
-
-
- # 没有返回值,禁用掉结果后端
- @celery.task
- def send_email_task(receiver_email,code): # 此时可以直接传邮箱,还能减少一次数据库的IO操作
- '''
- :param email: 接收消息的邮箱,用户的邮箱
- :return:
- '''
- # 启用线程发送邮件,此处最好加线程池
- ret = send_email(
- receiver_email=receiver_email,
- subject='登录验证码',
- message=f'您的验证码是:{code}',
- )
- return {'result':ret,receiver_email:code}
-
- @celery.task
- def cache_user_task():
- from apps.user.models import UserModel
- user = UserModel.query.all()
- lis = []
- for u in user:
- id = u.id
- name = u.name
- dic = {'id':id,'name':name}
- lis.append(dic)
- print(dic)
- cache.set('all-user-data',lis)
- return {'code':200,'msg':'查询数据成功'}
-
- @celery.task
- def test_check_fun():
- print('耗时开始:30秒')
- time.sleep(30)
- print('耗时结束...')
- return {'result':'异步任务执行后返回值','time':30}
-
- @celery.task(ignore_result=True)
- def save_file_task(file_bytes,file_path,delete_file_path=None):
- '''
- file_bytes: flask调用使用传递的文件字节流
- file_path: 文件保存的绝对路径
- 1、对与路径的拼接和文件名重复的逻辑,通通在视图函数中处理
- 2、在这里,只负责将文件保存到系统中,不管其他的逻辑
- '''
- #1、查看文件所在路径是否存在,不存在就创建
- file_path_dir = os.path.dirname(file_path)
- if not os.path.exists(file_path_dir):
- os.makedirs(file_path_dir)
-
- #2、保存文件
- with open(file_path, 'wb+') as f:
- f.write(file_bytes)
-
- #3、删除旧文件
- if delete_file_path:
- #想头像图片,一个用户只保存一个头像文件就可以了,删除之前旧的头像文件
- try:
- os.remove(delete_file_path)
- except Exception as _:
- print('删除失败')
- pass
4、定时任务模块:scheduler_task.py
- from celery_task.celery import celery
- import time
- '''
- 所有定时任务
- '''
-
- # 有返回值,返回值可以从结果后端中获取
- @celery.task
- def add_func(a, b):
- print('执行了加法函数',a+b)
- return a + b
-
-
- # 不需要返回值,禁用掉结果后端
- @celery.task(ignore_result=True)
- def cache_user_func():
- print('all')
-
-
5、校验任务是否成功:check_task.py
- from celery.result import AsyncResult
- from celery_task.celery import celery
-
- '''验证任务的执行状态的'''
-
-
- def check_task_status(task_id):
- '''
- 任务的执行状态:
- PENDING :等待执行
- STARTED :开始执行
- RETRY :重新尝试执行
- SUCCESS :执行成功
- FAILURE :执行失败
- :param task_id:
- :return:
- '''
- result = AsyncResult(id=task_id, app=celery)
- dic = {
- 'type': result.status,
- 'msg': '',
- 'data': None,
- 'code': 400
- }
- if result.status == 'PENDING':
- dic['msg'] = '任务等待中'
- elif result.status == 'STARTED':
- dic['msg'] = '任务开始执行'
- elif result.status == 'RETRY':
- dic['msg'] = '任务重新尝试执行'
- elif result.status == 'FAILURE':
- dic['msg'] = '任务执行失败了'
- elif result.status == 'SUCCESS':
- result = result.get()
- dic['msg'] = '任务执行成功'
- dic['data'] = result
- dic['code'] = 200
- # result.forget() # 将结果删除
- # async.revoke(terminate=True) # 无论现在是什么时候,都要终止
- # async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终止。
- return dic
-
写一个视图函数,接收用户上传的头像图片,将该图片保存到系统中,将保存的操作放到异步任务中。
- class UserOneResource(Resource):
-
- def put(self,id):
- '''
- 用户更新头像的接口
- '''
- file = request.files.get('picture')
- if not file:
- return NewResponse(error='请携带头像文件')
- user = models.UserModel.query.filter_by(id=id).first()
-
- if not user:
- return NewResponse(error='用户不存在')
- # print(file.filename,'=============')
- _,suffix = file.filename.split('.')
- #1、使用uuid创建文件名
- file_name = str(uuid.uuid4())+f'-{id}'+'.'+suffix
- file_path = os.path.join(STATIC_PATH,'picture',file_name).replace('\\','/')
- #2、读取出旧的头像路径
- delete_file_path = None
- if user.picture:
- picture = user.picture
- if picture[0] in ['\\','/']:
- picture = picture[1:]
- delete_file_path = os.path.join(BASE_PATH,picture).replace('\\','/')
- print(delete_file_path)
- #3、读取文件字节码
- file_bytes = file.read()
- #4、调用异步保存文件任务
- res = save_file_task.delay(file_bytes,file_path,delete_file_path)
- task_id = res.id
- #5、将头像新路径保存到数据库中
-
- picture = file_path.split('static/')[-1]
- picture = 'static/' + picture.replace('\\','/')
- user.picture = picture
- db.session.add(user)
- db.session.commit()
- return NewResponse(data={'task_id':task_id},msg='操作成功')
1、启动celery windows系统需要借助:eventlet celery -A celery_task.celery worker -l info -P eventlet linux系统: celery -A celery_task.celery worker -l info 2、启动定时任务 celery -A celery_task beat -l info