flask,celery实现定时发送邮件,参考了很多资料,结果都不详细,最终自己写了一个。。。。。

前言:

想实现一个简单的定时任务,因为用的是flask框架,所以最终决定使用 flask+celery 去实现定时发送邮件

因为celery的层级管理非常严格,踩了很多坑。。。

现在总结出来以供参考。。

1、版本依赖:

1 Flask                 2.1.1
2 Flask-Mail            0.9.1
3 celery                5.2.6
4 redis                 4.2.2
View Code

2、目录层级:

 

 

 3、详细内容:

遇到的bug:之前把 celery 实例 也放到app 下面了,导致出现了很多问题

问题1:flask app 和 celery 都成功启动,蓝图调用 task 任务失败,出现的问题要么是 task 任务没有被注册,要么就是 导入路径问题

问题2:flask app 和 celery 成功启动,蓝图也顺利传给 task,task 拿到任务之后,运行的时候用到了 flask-mail,最终提示的意思是 运行在了 应用上下文 之外,需要用 with app.app_context(): 来管理。。 总之一直是有问题。

最终能够成功运行的代码如下:

# sms 目录下

task01.py

 1 from celery_manage import celery_app
 2 from flask_mail import Mail,Message
 3 from flask import current_app
 4 
 5 
 6 @celery_app.task
 7 def send_email(data):
 8     mail = Mail(current_app)
 9     addr = data.get('addr')
10     title = data.get('title')
11     content = data.get('content')
12     msg = Message(title, recipients=[addr], body=content)
13     mail.send(msg)
14     print('ok')
View Code

# views.py 目录下

cattle.__init__.py

 1 @cattle.route('/ctime_email',methods=['GET','POST'])
 2 def ctime_email():
 3     if request.method=='GET':
 4         return render_template('cattle/index.html')
 5     else:
 6         from app01.sms.task01 import send_email
 7         data = request.json
 8         print(data)
 9         if data:
10             cdatelist = data.get('cdate').split('-')
11             ctimelist = data.get('ctime').split(':')
12             v1 = datetime.datetime(int(cdatelist[0]),int(cdatelist[1]),int(cdatelist[2]),int(ctimelist[0]),int(ctimelist[1]))
13             new_date = datetime.datetime.utcfromtimestamp(v1.timestamp()) # 将v1转成时间戳
14             print('newdata',new_date)
15             result = send_email.apply_async(args=[data],eta=new_date)
16             print(
17                 result.id,
18                 result
19             )
20         return 'ok'
View Code

# app01.__init__py

 1 def create_app(config_name='development',*args,**kwargs):
 2 
 3 
 4     app = Flask(__name__,static_folder=Config[config_name].STATIC_ROOT,static_url_path='/static')
 5     app.config.from_object(Config[config_name])
 6 
 7     auto_register_blueprint(app)
 8 
 9 
10     register_celery(celery=kwargs.get('celery'), app=app) # 注册celery
11 
12 
13     return app
14 
15 def register_celery(celery, app):
16     class ContextTask(celery.Task):
17         abstract = True
18 
19         def __call__(self, *args, **kwargs):
20             with app.app_context():
21                 return self.run(*args, **kwargs)
22 
23     celery.Task = ContextTask
View Code

# celery_manage.py

1 from celery import Celery
2 from app01 import celeryconfig
3 
4 celery_app = Celery('celery_worker', backend=celeryconfig.result_backend, broker=celeryconfig.broker_url, include=[
5     "app01.sms.task01",
6     "app01.sms.task02"
7 ])
8 celery_app.config_from_object(celeryconfig)
View Code

# manage.py

1 from app01 import create_app
2 from celery_manage import celery_app
3 from flask_script import Manager
4 
5 app = create_app(celery=celery_app)
6 manager = Manager(app)
7 
8 if __name__ == '__main__':
9     manager.run()
View Code

启动命令,都是在根目录下运行的:

启动flask:python manage.py runserver

启动celery:celery -A celery_manage worker -l info -P eventlet

4、重点说明:

celery 实例话时候的 incluede 参数
1 celery_app = Celery('celery_worker', backend=celeryconfig.result_backend, broker=celeryconfig.broker_url, include=[
2     "app01.sms.task01",
3     "app01.sms.task02"
4 ])
5 
6 # include 是告诉 celery 去 参数的路径下面寻找任务,值得注意的是,celery是从当前实例化的路径开始往下找
View Code

 

posted @ 2022-05-13 15:57  是小虫子啊  阅读(153)  评论(0编辑  收藏  举报