python定时任务/异步任务工具celery与Scheduler

python定时任务/异步任务工具celery与Scheduler


修改时间修改说明修改人
2018-07-01初次成稿AyoCross

本文以django/flask为例,介绍如何使用celery or Scheduler 来实现python的定时任务计划,并比较二者在使用过程中的不同之处,区分不同场景来选择适当的工具。

1. why 异步 or 定时

异步任务,指的是与同步任务相反的一种逻辑处理流程,在执行某任务A的过程中,需要执行任务B,但是B任务比较耗时,而且B的结果对A无影响。这种情况下,就需要考虑使用异步来实现任务B,从而使A的处理速度加快。一个典型的例子就是用户操作过程中的邮件提醒,比如业务逻辑如下:当用户修改web项目中的某几个敏感参数的时候,需要向该用户以及管理员用户发送通知邮件,并完成参数的修改。那么用户的修改操作即可看作是A任务,邮件发送即为B任务,B任务的发送结果(邮件发送成功还是失败)对修改任务A是否成功执行无影响。此时就需要考虑将B邮件发送任务异步实现,减少修改参数的服务器处理速度,提高用户体验。

定时任务,就更好理解了,比如需要每隔5分钟检查一次数据库里的当天有效数据,如果发生变更,则需要执行某些逻辑。此时就需要定时任务来执行响应的任务计划。

how to

1. Scheduler

Scheduler作为一个轻量级的定时任务工具,在小型项目或者轻量级异步任务中经常会用到,最常见的使用方法如下:
首先,安装Scheduler:pip install APScheduler
然后,在项目启动的位置,调用以下函数:

from apscheduler.schedulers.background import BackgroundScheduler

def initialize_cron():
    apsched = BackgroundScheduler()
    apsched.add_job(isp_sync , 'cron' , kwargs = {} , hour = 2 , minute = 10)
    apsched.add_job(kafka_sync,'cron', hour = 6 , minute = 10)
    apsched.add_job(workflow,'cron', minute = '*/10')
    apsched.add_job(detect_isp_access,'cron', hour='*')
    apsched.add_job(mass_report,'cron', minute = '*/5')
    apsched.start()

感觉似乎没什么好介绍的,只要部署上去,就完事了,它会每隔多少时间,或者每天的定点时间,去执行响应的任务。比较高阶的用法,会出现在某些特殊的任务,可能需要在某个时间段内暂停任务,然后再恢复任务。就会需要用到pause / resume,详情可以查看这篇博文,写的很详细。

2. celery

只要是项目上了点规模,或者说,既有定时任务,也有异步任务需要;再或者,待执行的任务占资源比较多,那就最好使用celery来实现,可以把任务从主任务中剥离出来,减少项目占用资源过多出问题的几率。
在django/flask中使用celery,也是非常简单的,首先,安装celery,

pip install celery==3.1.25
pip install redis==2.10.5  # Broker,用于存放celery任务队列,除celery之外,也可使用rabbitMQ/Zookeeper等

安装完毕后,需要在项目中配置celery,以flask为例:在创建flask_app时,对celery进行配置:


app = Flask("cdn_console",static_folder='', static_url_path='')
app.config.from_object('DefaultConfig')
cdn_celery = configure_celery(app)

def configure_celery(app):
    platforms.C_FORCE_ROOT = True
    celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL'])
    celery.conf.update(app.config)
    Taskbase = celery.Task

    class ContextTask(Taskbase):
        abstract = True

    def __call__(self, *args, **kw):
        with app.app_context():
            return Taskbase.__call__(self, *args, **kw)

    celery.Task = ContextTask
    return celery


class DefaultConfig(object):
    DEBUG = True
    TESTING = False

    _basedir = os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(
                            os.path.dirname(__file__)))))

    if DEBUG==True:
        redis_host='127.0.0.1'
    else:
        redis_host='redis.test.org'

    REDIS_URL = "redis://%s:6379/1"%redis_host
    CELERY_BROKER_URL = 'redis://%s:6379'%redis_host,  # Broker 地址
    # CELERY_RESULT_BACKEND = 'redis://localhost:6379',  # 结果存储地址
    
    # 定时任务,
    CELERYBEAT_SCHEDULE = {
        'task1': {  # 定时任务1,每隔5分钟执行一次
            'task': 'tasks.api_delay_func_1',
            "schedule": timedelta(minutes=5),
            "args": '',
        },
        #'task2': {
        #    'task': 'tasks.api_delay_func_2',
        #    "schedule": timedelta(minutes=30),
        #    "args": '',
        #},
    }
} 


# tasks.py中的定时任务:
@cdn_celery.task
def api_delay_func_1(log_type='5'):
    """
    定时任务
    """
    from run import app
    app_ctx = app.app_context()
    app_ctx.push()
    try:
        print '定时任务'
        app_ctx.pop()
    except Exception as e:
        app_ctx.pop()
        print e   

配置中之所有有个ContextTask函数,是因此flask的应用上下文,这种配置也是flask官方推荐的配置模块。

如果某个函数需要执行异步任务,添加一个装饰器即可:

@cdn_celery.task
def flow_converge():
    """
    流量/带宽  汇聚
    """
    print '异步任务'
    return


# 在需要调用该异步函数的位置,使用delay来调用
from tasks import flow_converge
flow_converge.delay()

项目中添加完毕之后,在项目所在的目录运行脚本来执行celery:

nohup celery worker -A tasks --loglevel=info -c 2 >../celery_worker.log 2>&1 &
nohup celery beat -A tasks --loglevel=info >../celery_beat.log 2>&1 &

celery架构

  • Celery Beat,任务调度器,Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列
  • Celery Worker,执行任务的消费者 Broker,消息代理,接受生产者的任务消息,存进队列然后按序发送给消费者
  • Producer,定时任务或者调用了API产生任务交给任务队列进行处理 Result Backend,任务处理完后保存状态信息和结果

终止执行中的celery任务

from celery.task.control import inspect
inspect().active()

这将列出正在处理的活动任务的列表。 你可以在那里获得任务的id 。 一旦获得了任务的id,就可以通过使用

from celery.task.control import revoke
revoke(task_id, terminate=True)

3. which one

只要不是太小的项目,或者你确定项目以后不会有其他的定时任务出现了,那么就使用scheduler来实现,否则,加入celery的怀抱吧!毕竟celery真的是太好用了。。

AyoCross 20180701

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值