http://bbs.7boo.org/forum.php?mod=viewthread&tid=14546

如果将定时任务部署在一台服务器上,那么这个定时任务就是整个系统的单点,这台服务器出现故障的话会影响服务。对于可以冗余的任务(重复运行不影响服务),可以部署在多台服务器上,让他们同时执行,这样就可以很简单的避免单点。但是如果任务不允许冗余,最多只能有一台服务器执行任务,那么前面的方法显然行不通。本篇文章就向大家介绍如何避免这种互斥任务的单点问题,最后再介绍一下基于APScheduler的分布式定时任务框架,这个框架是通过多个项目的实践总结而成的。



        对于运行在同一台服务器上的两个进程,可以通过加锁实现互斥执行,而对于运行在多个服务器上的任务仍然可以通过用加锁实现互斥,不过这个锁是分布式锁。这个分布式锁并没有那么神秘,实际上只要一个提供原子性的数据库即可。比如,在数据库的locks表里有一个记录(lock record),包含属性:



        name:锁的名字,互斥的任务需要用名字相同的锁。



        active_ip:持有锁的服务器的ip。



        update_time:上次持有锁的时间,其他非活跃的服务器通过这个属性判断活跃的服务器是否超时,如果超时,则会争夺锁。



        一个持有锁的服务器通过不断的发送心跳,来更新这个记录,心跳的内容就是持有锁的时间戳(update_time),以及本机ip。也就是说,通过发送心跳来保证当前的服务器是活跃的,而其他服务器通过lock record中的update_time来判断当前活跃的服务器是否超时,一旦超时,其他的服务器就会去争夺锁,接管任务的执行,并发送心跳更新active_ip。



        通过上面描述,这个框架中最重要的两个概念就是分布式锁和心跳。下面看一下分布式定时任务框架中是如何实现这两点的。当然,这个框架依赖于APScheduler,所以必须安装这个模块,具体APScheduler的介绍见我的另一篇文章,因为依赖APScheduler,所以这个框架很简单,只有一个类:



from apscheduler.scheduler import Scheduler

import datetime

import time

import socket

import struct

import fcntl



def get_ip(ifname):

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    return socket.inet_ntoa(fcntl.ioctl(

        s.fileno(),

        0x8915, # SIOCGIFADDR

        struct.pack('256s', ifname[:15])

    )[20:24])

    



class MutexScheduler(Scheduler):

    def __init__(self, gconfig={}, **options):

        Scheduler.__init__(self, gconfig, **options)

        self.ip = get_ip('eth0')



    def mutex(self, lock = None, heartbeat = None, lock_else = None, 

            unactive_interval = datetime.timedelta(seconds = 30)):

        

        def mutex_func_gen(func):

            def mtx_func():



                if lock:

                    lock_rec = lock()

                    now = datetime.datetime.now()



                    # execute mutex job when the server is active, or the other server is timeout.

                    if not lock_rec or lock_rec['active_ip'] == self.ip or (lock_rec['update_time'] and now - lock_rec['update_time'] >= unactive_interval):  

                        if lock_rec:

                            del lock_rec['active_ip']

                            del lock_rec['update_time']



                        if not lock_rec:

                            lock_rec = {}



                        lock_attrs = func(**lock_rec)

                        if not lock_attrs:

                            lock_attrs = {}



                        # send heart beat

                        heartbeat(self.ip, now, **lock_attrs)

                    else: 

                        lock_else(lock_rec)

                else:

                    func()



            return mtx_func



        self.mtx_func_gen = mutex_func_gen



        def inner(func):

            return func



        return inner



    def cron_schedule(self, **options):

        def inner(func):

            if hasattr(self, 'mtx_func_gen'):

                func = self.mtx_func_gen(func)



            func.job = self.add_cron_job(func, **options)

            return func

        return inner



        mutex方法是核心,通过装饰器的方式提供互斥功能。在使用时:



@sched.mutex(lock = my_lock, heartbeat = my_heartbeat)

@sched.cron_schedule(second = '*')

def my_job(**attrs):

    print 'my_job ticks'



        mutex装饰器必须用在cron_schedule装饰器之前,mutex主要是组装job。mutex的参数有:



                lock:函数,用于获取锁记录(lock record),函数原型:lock()。lock的返回值时dict,就是锁记录内容。



                heartbeat:函数,用于发出心跳,函数原型:heartbeat(ip, now, **attrs)。ip是本机ip;now是当前时间戳;attrs是一个dict,用于在锁记录中存放一些其他用户自定义信息。



                lock_else:函数,在没有获得锁时执行,函数原型:lock_else(lock_rec)。lock_rec是锁记录,包含active_ip,update_time以及用户自定义的属性。



                unactive_interval:datetime.timedelta类型,超时时间,也就是说当前时间减去update_time大于unactive_interval的话,就代表超时。



        在使用这个类时,必须实现自己的lock,heartbeat以及lock_else函数。



        job的原型是job(**attrs),attrs就是存放在锁记录中的用户自定义属性,job可以有dict类型的返回值,这个返回值会存入锁记录中。



        下面,看一下具体使用的例子,使用的mongodb存放分布式锁。



import apscheduler.events

import datetime

import time

import pymongo

import sys



sys.path.append('../src/')



import mtxscheduler



sched = mtxscheduler.MutexScheduler()



mongo = pymongo.Connection(host = '127.0.0.1', port = 27017)

lock_store = mongo['lockstore']['locks']



def lock():

    print 'lock()'

    now = datetime.datetime.now() - datetime.timedelta(seconds = 3)

    lck = lock_store.find_one({'name': 't'})

    return lck



def hb(ip, now, **attrs):

    print 'heartbeat()'

    attrs['active_ip'] = ip

    attrs['update_time'] = now

    lock_store.update({'name': 't'}, {'$set': attrs}, upsert = True)



def le(lock_rec):

    if lock_rec:

        print 'active ip', lock_rec['active_ip']

    else:

         print 'lock else'



i = 0



@sched.mutex(lock = lock, heartbeat = hb, lock_else = le)

@sched.cron_schedule(second = '*')

def job(**attr):

    global i

    i += 1

    print i



def err_listener(ev):

    if ev.exception:

        print sys.exc_info()

            

sched.add_listener(err_listener, apscheduler.events.EVENT_JOB_ERROR)



sched.start()

time.sleep(10)



        这里用到了mongodb的python driver,可以通过命令安装:



easy_install pymongo



        easy_install的安装件另一篇文章。



        这个任务很简单就是定时打印整数序列。同时在两台服务器上部署运行,可以发现只有一台服务器会输出整数序列。



        使用起来还是很方便的。源代码见github,其中还有使用redis存储锁,已经在锁记录中存放自定义信息的例子。

分布式定时任务框架——python定时任务框架APScheduler扩展的更多相关文章

  1. python web框架——扩展Django&tornado

    一 Django自定义分页 目的:自定义分页功能,并把它写成模块(注意其中涉及到的python基础知识) models.py文件 # Create your models here. class Us ...

  2. Python 定时任务框架 APScheduler 详解

    APScheduler 最近想写个任务调度程序,于是研究了下 Python 中的任务调度工具,比较有名的是:Celery,RQ,APScheduler. Celery:非常强大的分布式任务调度框架 R ...

  3. Python定时任务框架APScheduler 3.0.3 Cron示例

    APScheduler是基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以持久化任务.基 ...

  4. Python定时任务框架APScheduler

    http://blog.csdn.net/chosen0ne/article/details/7842421 APScheduler是基于Quartz的一个Python定时任务框架,实现了Quartz ...

  5. [转]Python定时任务框架APScheduler

    APScheduler是基于Quartz的 一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以 持久化任务 ...

  6. APScheduler(python 定时任务框架)最简单使用教程

    有时候需要部署一些很简单的python定时任务,使用APScheduler是很好的选择.只需要简单的设置几个参数,就可以实现定时.定分甚至秒来跑. 第一步:用pip安装APScheduler pip ...

  7. [Dynamic Language] Python定时任务框架

    APScheduler是一个Python定时任务框架,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以持久化任务.并以daemon方式运行应用. 在APSchedu ...

  8. Python定时框架 Apscheduler 详解【转】

    内容来自网络: https://www.cnblogs.com/luxiaojun/p/6567132.html 在平常的工作中几乎有一半的功能模块都需要定时任务来推动,例如项目中有一个定时统计程序, ...

  9. python 定时任务APScheduler 使用介绍

    python 定时任务APScheduler 使用介绍   介绍: APScheduler的全称是Advanced Python Scheduler.它是一个轻量级的 Python 定时任务调度框架. ...

随机推荐

  1. PS 图像调整算法——饱和度调整

    算法参考自 阿发伯 的博客. http://blog.csdn.net/maozefa 饱和度调整 图像的饱和度调整有很多方法,最简单的就是判断每个象素的R.G.B值是否大于或小于128,大于加上调整 ...

  2. 和菜鸟一起学linux总线驱动之i2c死锁问题

    不知不觉中已经有好几个月没有写点东西了,懒了就是懒了,说是忙着想把产品做得更好,都是借口,每天花一点时间来写点东西确实很不错,自己也坚持了很久很久,只不过今年以来,发现提高不是很大,能写的东西好少好少 ...

  3. leetcode之旅(6)-Add Digits

    题目: Given a non-negative integer num, repeatedly add all its digits until the result has only one di ...

  4. Oracle 中Return 和exit的区别

    在Oracle存储过程中,使用Return 时,如果执行到Return语句,会跳出整个语句(如果是循环,会跳出整个循环),将不再执行,也就是结束了整个存储过程. 下面就用一个例子来说明一下 ,这个存储 ...

  5. Xcode使用心得02:如何在项目中关闭ARC特性

    在obj-c系列内存管理的博文里大家应该对ARC有所了解,一般是不推荐关闭ARC特性的,但你也保不齐啥时候有这个需求,于是乎我们看看在最新的x6b中如何将其关闭吧. 因为Build Seting里的子 ...

  6. codeblocks设置代码黑色主题

    说明 网上资料较杂乱,特整理以备留用和他人参阅. 配置文件下载 首先下载配置文件. 配置文件 将配置文件拷到系统盘codeblocks配置路径而非安装路径. win10下路径:C:\Users\用户名 ...

  7. jQuery学习小结

    1.jQuery hide() 和 show() 通过 jQuery,您可以使用 hide() 和 show() 方法来隐藏和显示 HTML 元素: $("#hide").clic ...

  8. J2EE架构师之路

    不经意的回首,工作进入第五个年头了,发现走过了从Java程序员到J2EE架构师的历程. 发现电脑上安装了各种各样的J2EE工具:JBuilder, WSAD, Eclipse, Rose, Toget ...

  9. unity零基础开始学习做游戏(三)鼠标输入,来个虚拟摇杆怎么样?

    -------小基原创,转载请给我一个面子 现在移动游戏越来越火,大家都拿手机平板玩游戏,没有键盘和手柄输入,所以就不得不看看虚拟摇杆怎么搞?(小基对于没有实体反馈不是很喜欢呢) 首先要清楚,鼠标操作 ...

  10. vfd折腾(二)

    这篇是前期程序部分,主要讲驱动pt6311的程序 电路见上一篇博文 #ifndef PT6311_H #define PT6311_H #include "sys.h" #incl ...