celery 是一种分布式任务队列
以下是需要理解的几种概念
任务:消息队列里面的一个工作单元
分布式:独立Worker可以布在不同的机器上,一个worker可以指定并发数
Broker:消息通讯的中间人,主要有RabbitMQ, Redis(本例用的是redis,较为轻量级)
beat:定时任务,可以指定任务若干秒后,或定时时间执行
安装就不说了,直接用 pip 就可以了,也可以安装在venv上
先创建一下结构
celery_proj/
__init__.py
beat_tasks.py #定时任务放在这里
celery.py #启动脚本
config.py #配置
tasks.py #非定时任务,手动调的等其他任务放在这个里
celery.py
#!/mfs/lib/Envs/whx/bin/python2.7
# -*-coding:utf-8 -*-
# Created on 2016-05-09
# @author: whx
from __future__ import absolute_import
from celery import Celery
#指定任务py
app = Celery('celery_proj', include=['celery_proj.tasks', 'celery_proj.beat_tasks'])
#指定配置
app.config_from_object('celery_proj.config')
#def getActiveInfo(timeout=1):
# active_dict = app.control.inspect(timeout=timeout).active()
# return active_dict
#def removeTask(task_id):
# print app.control.revoke(task_id, terminate=True)
if __name__ == '__main__':
app.start()
beat_tasks.py
#!/mfs/lib/Envs/whx/bin/python2.7
# -*-coding:utf-8 -*-
# Created on 2016-05-09
# @author: whx
from __future__ import absolute_import
from celery_proj.celery import app
import datetime
import subprocess
import os
def run_cmd(cmd):
"运行系统命令"
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = pipe.communicate()
errcode = pipe.wait()
if errcode != 0:
msg = "command failed:\n%s\n" % ' '.join(cmd)
if stdout:
msg += "Standard output:\n%s\n" % stdout
if stderr:
msg += "Standard error:\n%s\n" % stderr
raise OSError, msg
return stdout, stderr
def get_last_hour():
return (datetime.datetime.today() - datetime.timedelta(hours=1)).strftime("%Y%m%d%H")
#定义每小时日志ETL任务
@app.task(name='log_clean')
def log_clean_traffic():
yyyymmddhh = get_last_hour()
tag_dir= os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
cmd = "bash %s/run_sh/logformat.sh traffic %s"%(tag_dir, yyyymmddhh)
#print cmd
run_cmd(['sh', '-c', cmd])
tasks.py
from __future__ import absolute_import
from celery_proj.celery import app
#定义任务超时时间
#bind 绑定一个任务实例,self
#max_retries 是重新尝试的次数
#default_retry_delay 任务重新尝试的时间间隔6秒
#异常处理的countdown,时间间隔10秒,优先于default_retry_delay
#expires:任务运行的时间限制,超过300秒,任务会直接revoke
#没有指定任务名默认为dologic
@app.task(bind=True, max_retries=3,default_retry_delay=6, expires=300)
def dologic(self, task_name, args_json):
cmd = "bash /mfs/home/whx/Script/spark/spark_run.sh %s"%(task_name, args_json)
try:
return run_cmd(['sh', '-c', cmd])
except Exception as exc:
raise self.retry(exc=exc, countdown=10)
config.py
#!/mfs/lib/Envs/whx/bin/python2.7
# -*-coding:utf-8 -*-
# Created on 2016-05-09
# @author: whx
from __future__ import absolute_import
from celery.schedules import crontab
#执行结果保存的路径
CELERY_RESULT_BACKEND = 'redis://:pwd@host:port/0'
#中间人
BROKER_URL = 'redis://:pwd@host:port/12'
#序列化
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT=['json']
#结果保存的时间
CELERY_TASK_RESULT_EXPIRES = 86400 # 24 hours.
CELERY_TIMEZONE = 'Asia/Shanghai' # 时区
#queue 定义执行的队列,启动celery时可以指定队列名
CELERY_ROUTES = {
'celery_proj.tasks.dologic': {'queue': 'cq_170'},
'log_clean': {'queue': 'cq_170'},
}
CELERYBEAT_SCHEDULE = {
#定义每小时的10分执行
'log_clean': {
"task": "log_clean",
"schedule": crontab(minute=10),
"args": ()
},
}
另外的:
依赖任务用chain,执行结果可以一级级往下传
并列任务用group
from celery import task, group, chain
@task
def add(x, y):
return x + y
@task(name="main1")
def test1():
#链级 结果向下传递: 2 + 2 + 4 + 8 = 16
res = chain(add.s(2, 2), add.s(4), add.s(8))()
res = (add.s(2, 2) | add.s(4) | add.s(8))()
#链级 结果没有传递:9
res = chain(add.si(2, 2), add.si(4, 4), add.si(1, 8))()
res = (add.si(2, 2) | add.si(4, 4) | add.si(1, 8))()
return res
@task(name="main2")
def test2():
#并列执行
join = group([
add.s(2,2),
add.s(4,4),
add.s(8,8),
add.s(16,16),
])()
return res
#先依赖后并行
#res = (add.s(4, 4) | group(add.si(i, i) for i in xrange(10)))()
调试运行:
python -m celery -A celery_proj worker -E -B -Q cq_170 -l info --concurrency=10 -n worker1@%h
#启动worker
#concurrency 并发数
#-E 启动事件
#-B 同时启动beat,既 定时任务
#-Q 指定队列名:cq_170
#%h worker1@%h worker1@george.example.com
#%n worker1@%n worker1@george
#%d worker1@%d worker1@example.com
后台运行:
python -m celery multi start whxpc -A celery_proj -E -B -Q cq_170 -c10 -n worker1@%h -l debug \
--logfile="./log/celery.log" --pidfile="./celery.pid"
官方文档:http://docs.celeryproject.org/en/latest/