Celery #4 结合Flask和apscheduler使用

更进一步地,我们希望可以用一个web服务调用Celery,这样我们可以改通过web服务查看任务的执行。

Celery本身可以用beat定时,也提供了一定的状态监视手段。但我们需要动态更改任务计划和持久化任务记录时,自建一套逻辑也许更加方便。

具体方案

思路:
实现一个Flask实例作为web服务,通过apscheduler在web进程中实现定时计划。
任务执行前后,将相关时间、状态、异常、结果等写入mongoDB保存。
通过web服务查看celery转台、任务、任务执行记录等。

  • 由于不再依赖消息队列获取结果,可以不设置backend。
  • 这样设计的问题是,不能捕捉到celery并发限制后,位于排队序列里的任务。记录的是任务实际开始执行的时间。

现在目录下有文件:

tasks.py
admin.py

其内容:

# tasks.py
import time
import functools
from pymongo import MongoClient
from celery import Celery

conn = MongoClient()
log = conn.tasklog.log

app = Celery('tasks', broker='redis://localhost:6379/9')

def task_log(func):
    '任务装饰器。向mongo写入状态信息'
    @functools.wraps(func) 
    def wrapper(*args, **kargs):
        task_name = func.__name__
        log_id = log.insert({
            'name': task_name,
            'start_time': int(time.time() * 1000),
            'end_time': 0,
            'status': 0,
        })
        try:
            result = func(*args, **kargs)
            log.update({
                '_id': log_id
            }, {
                '$set': {
                    'end_time': int(time.time() * 1000),
                    'status': 1,
                    'result': str(result)
                }
            })
            return result
        except Exception as e:
            print(e)
            log.update({
                '_id': log_id
            }, {
                '$set': {
                    'end_time': int(time.time() * 1000),
                    'status': 2,
                    'err': str(e)
                }
            })
    return wrapper

@app.task
@task_log
def add(x, y):
    print('{} add {} equals {}'.format(x, y, x+y))
    return x + y
# admin.py
from celery.local import PromiseProxy
from flask import Flask, request
from apscheduler.schedulers.background import BackgroundScheduler
from pymongo import MongoClient

from utils.mkresp import mkresp
import tasks

conn = MongoClient()
log = conn.tasklog.log

# 读取所有任务
TASKS = {}
for attrname in dir(tasks):
    attr = getattr(tasks, attrname)
    if type(attr) == PromiseProxy:
        TASKS[attrname] =  attr

# 定时任务计划
sched = BackgroundScheduler()
#每分钟执行一个add任务
sched.add_job(tasks.add.delay, 'cron', minute='*', args=(5,5))
sched.start()

# Flask app
app = Flask(__name__)

@app.route('/')
def r_index():
    return 'admin'

@app.route('/tasks')
def r_tasks():
    return mkresp(msg=list(TASKS.keys()))

@app.route('/tasklogs')
def r_tasklogs():
    docs = list(log.find())
    return mkresp(msg=docs)

@app.route('/taskrun/')
def r_task_run(taskname):
    params = request.values.get('params')
    params = json.loads(params)
    print(params)
    task = TASKS.get(taskname)
    if not task:
        return mkresp(code=500, msg='不存在指定任务')
    task.delay(*params)
    return mkresp()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9000)

使用celery -A tasks worker --loglevel=info -P gevent启动celery,再运行admin.py。

现在,访问http://localhost:9000/tasks即可看到所有任务。
访问http://localhost:9000/tasklogs即可看到所有任务执行记录。
访问http://localhost:9000/taskrun/add?params=[1,2]即可立即执行add任务,参数1,2。

你可能感兴趣的:(Celery #4 结合Flask和apscheduler使用)