在网站A业务中有个操作 是 要在网站B中新建一台服务器跑业务。A中执行B中的接口创建服务器 中间需要的时间很长。A如果一直等着B放回结果会超时。B 执行命令 很耗资源,而且不能执行太多的并发。 这这种需求下 我们想到的就是 传说中的 “消息列队“
来解决这种分布式事务。
1 Celery介绍
Celery是一个异步的任务队列(也叫做分布式任务队列)
Celery分为3个部分
(1)worker部分负责任务的处理,即工作进程(我的理解工作进程就是你写的python代码,当然还包括python调用系统工具功能)
(2)broker部分负责任务消息的分发以及任务结果的存储,这部分任务主要由中间数据存储系统完成,比如消息队列服务器RabbitMQ、redis、
Amazon SQS、MongoDB、IronMQ等或者关系型数据库,使用关系型数据库依赖sqlalchemy或者django的ORM
(3)Celery主类,进行任务最开始的指派与执行控制,他可以是单独的python脚本,也可以和其他程序结合,应用到django或者flask等web框架里面以及你能想到的任何应用
celery的模块架构
工作原理
Celery Flower
Flower是基于Celery web的监控和管理
celery的运用比较简单:
1.安装celery
2 创建一个celery application 用来定义你的任务列表
3.编写需要异步执行的任务函数,并用celery实例的task修饰器修饰
4.调用异步任务时, 用函数名.delay(参数)形式调用为异步调用。 函数名(参数)方式为同步调用。
5.执行celery监听服务
2 安装celery
source ~/python3.6_env/bin/activate
(python3.6_env) $ pip install celery==4.1.0
(python3.6_env) $ pip install redis
安装celery flower
(python3.6_env) $ pip install flower
安装django-celery
(python3.6_env) $pip install django-celery
我们总在说c10k的问题, 也做了不少优化, 然后优化总是不够的。
其中的一个瓶颈就是一些耗时的操作(网络请求/文件操作--含耗时的数据库操作)。
如果我们不关心他们的返回值,则可以将其做成异步任务,保证执行成功即可。
3 Celery使用案例
案例1 任务调度执行:
(1)创建一个celery application 用来定义你的任务列表
编译一个tasks.py 文件
#!/usr/bin/env python # encoding: utf-8 from celery import Celery from time import sleep # broker="redis://redis:6379/1" 列队数据库存放点 # backend="redis://redis:6379/2" 任务执行完成后数据库存放点 //创建一个celery application 用来定义你的任务列表 app=Celery("mtasks",broker="redis://127.0.0.1:6379/1", backend="redis://127.0.0.1:6379/2") @app.task def add(x,y): return x + y @app.task def mult(x,y): sleep(10) return x * y @app.task def getname(name): return name
(2)启动Celery Worker来开始监听并执行任务
celery -A tasks worker --loglevel=info
(3)服务使用方调用任务
再打开一个终端, 进行命令行模式,调用任务
add.delay(6, 5)
<AsyncResult: a324e552-90db-4e12-8b97-1539471c4bb2>
观察服务端变化
案例2. Celery实现定时器
from celery import Celery from celery.schedules import crontab app = Celery('tasks', broker='redis://localhost:6379/0') #@app.on_after_configure.connect @app.on_after_configure.connect def setup_periodic_tasks(sender, **kwargs): # Calls test('hello') every 10 seconds. sender.add_periodic_task(10.0, test.s('hello'), name='add every 10') # Calls test('world') every 30 seconds sender.add_periodic_task(30.0, test.s('world'), expires=10) # Executes every Monday morning at 7:30 a.m. sender.add_periodic_task(crontab(hour=7, minute=30, day_of_week=1), test.s('Happy Mondays!'),) @app.task def test(arg): print(arg)
开启两个终端
(1)一个终端启动worker
celery -A tasks -l INFO worker
(2)另一个终端启动调度器 beat (组件 beat,它用于对任务进行调度)
celery -A tasks -l INFO beat
为了快速部署并启动服务,我们可以写一个Celery管理脚本程序
celery管理脚本
#!/bin/bash ############################################################# #celery flower web ui controller script # web reference : https://my.oschina.net/u/2306127/blog/420929 ############################################################ CELERY_UI_PORT=10055 BROKER="redis://127.0.0.1:6379/1" #BROKER="redis://172.16.245.100:6379/1" APP_NAME="worker" #start the celery ui web server. start_celery_flower() { nohup celery flower --broker=$BROKER --port=$CELERY_UI_PORT >./flower.log 2>./flower.log & #nohup celery flower --broker=$BROKER --port=$CELERY_UI_PORT >/dev/null 2>/dev/null & #celery flower --broker=$BROKER --port=$CELERY_UI_PORT -A $APP_NAME & echo "start celery ui server ok with $CELERY_UI_PORT" } #start the celery app start #params: # -A app name # -l log level # -c concurrency num start_celery_app() { #celery worker --workdir tasks -A $APP_NAME -l info nohup celery worker --workdir tasks -A $APP_NAME -l info -c 2 >./app.log 2>.app.log & #celery worker -A $APP_NAME -l info -c 5 } test_app(){ python tasks/client.py } stop_celery_app(){ killall celery } case $1 in flower) start_celery_flower ;; app) start_celery_app ;; test) test_app ;; stop) stop_celery_app ;; help) echo "./celery_ctl.sh flower | app | test| stop " ;; *) echo "./celery_ctl.sh flower | app | test | stop " exit 2 esac