在写个人博客项目的时候,涉及到邮件验证码和短信验证码的发送的功能,应该异步地去执行,而不是同步形成阻塞。这时就要用到python的扩展库celery。
celery是一个强大的分布式任务队列的异步处理组件,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。
Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。
消息中间件:
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
任务执行单元:
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
任务结果存储:
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
个人理解,有些功能,比如发短信通知,是一个耗时任务,如果都扎堆在一起去执行,同步操作一次执行一个,一次发一条短信,势必会形成阻塞。例如美团外卖,中午12点整的下单人数100个,每个人下单完成后系统都要发个短信给用户说“下单成功”,每次发短信时间假如是1s,那就要100s发完这100条短信,那第101个人下单完成后要等100秒才能收到短信,这是什么用户体验?很显然,我们不能一次发一条短信,而应该并发,一次发N条。这些并发技术是比较底层的,写起来很麻烦,而celery就帮我们封装好了这些并发技术,我们要做的就是通过celery去调动执行并发。
使用场景:
异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
定时任务:定时执行某件事情,比如每天数据统计
之前在cms后台修改邮箱的时候点击发送验证码邮件,会有大概1.2s的延时然后弹出发送成功的对话框。这个延时就是调用flask扩展mail发送邮件的耗时,这时在同步场景下执行的。现在换成celery的异步操作:
1 from celery import Celery 2 from flask_mail import Message 3 from exts import mail 4 from flask import Flask 5 import config 6 app = Flask(__name__) 7 app.config.from_object(config) 8 mail.init_app(app) # 初始化app 9 10 def make_celery(app): 11 celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],broker=app.config['CELERY_BROKER_URL']) 12 celery.conf.update(app.config) 13 TaskBase = celery.Task 14 class ContextTask(TaskBase): 15 abstract = True 16 def __call__(self, *args, **kwargs): 17 with app.app_context(): 18 return TaskBase.__call__(self, *args, **kwargs) 19 celery.Task = ContextTask 20 return celery 21 22 celery = make_celery(app) 23 24 @celery.task 25 def send_mail(subject,recipients,body): 26 message = Message(subject=subject,recipients=recipients,body=body) 27 mail.send(message)
之后点击发送验证码就是立即弹出alert了。用户体验极佳。
这里要注意,目前我这个项目运行在window10上,但是celery对windows不太友好,4.x版本后不支持windows,要用eventlet去解决。eventlet是Python的并发网络库。
在命令行开启:celery -A tasks.celery worker --pool=eventlet --loglevel=info
后来发现一定几率出现莫名的报错。
将eventlet改为solo就可以了:
celery -A tasks.celery worker --pool=solo --loglevel=info