分布式定时任务框架——python定时任务框架APScheduler扩展
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扩展的更多相关文章
- python web框架——扩展Django&tornado
一 Django自定义分页 目的:自定义分页功能,并把它写成模块(注意其中涉及到的python基础知识) models.py文件 # Create your models here. class Us ...
- Python 定时任务框架 APScheduler 详解
APScheduler 最近想写个任务调度程序,于是研究了下 Python 中的任务调度工具,比较有名的是:Celery,RQ,APScheduler. Celery:非常强大的分布式任务调度框架 R ...
- Python定时任务框架APScheduler 3.0.3 Cron示例
APScheduler是基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以持久化任务.基 ...
- Python定时任务框架APScheduler
http://blog.csdn.net/chosen0ne/article/details/7842421 APScheduler是基于Quartz的一个Python定时任务框架,实现了Quartz ...
- [转]Python定时任务框架APScheduler
APScheduler是基于Quartz的 一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以 持久化任务 ...
- APScheduler(python 定时任务框架)最简单使用教程
有时候需要部署一些很简单的python定时任务,使用APScheduler是很好的选择.只需要简单的设置几个参数,就可以实现定时.定分甚至秒来跑. 第一步:用pip安装APScheduler pip ...
- [Dynamic Language] Python定时任务框架
APScheduler是一个Python定时任务框架,使用起来十分方便.提供了基于日期.固定时间间隔以及crontab类型的任务,并且可以持久化任务.并以daemon方式运行应用. 在APSchedu ...
- Python定时框架 Apscheduler 详解【转】
内容来自网络: https://www.cnblogs.com/luxiaojun/p/6567132.html 在平常的工作中几乎有一半的功能模块都需要定时任务来推动,例如项目中有一个定时统计程序, ...
- python 定时任务APScheduler 使用介绍
python 定时任务APScheduler 使用介绍 介绍: APScheduler的全称是Advanced Python Scheduler.它是一个轻量级的 Python 定时任务调度框架. ...
随机推荐
- OpenCV——PS 滤镜, 浮雕效果
具体的算法原理可以参考: PS 滤镜, 浮雕效果 // define head function #ifndef PS_ALGORITHM_H_INCLUDED #define PS_ALGORITH ...
- Oracle rownum 分页, 排序
Oracle rownum 分页, 排序 什么是rownum, rownum的生成, rownum相关的符号操作 Rownum是oracle生成结果集时得到的一个伪列, 按照读出行的顺序, 第一条ro ...
- 和菜鸟一起学linux之linux性能分析工具oprofile移植
一.内核编译选项 make menuconfig General setup---> [*] Profiling support <*> OProfile system profil ...
- linux 常见命令及说明杂记
systemctl 命令: systemctl 是管制服务的主要工具, 它整合了chkconfig 与 service功能于一体.示例:systemctl is-enabled iptables.se ...
- 使用 Helm - 每天5分钟玩转 Docker 容器技术(163)
Helm 安装成功后,可执行 helm search 查看当前可安装的 chart. 这个列表很长,这里只截取了一部分.大家不禁会问,这些 chart 都是从哪里来的? 前面说过,Helm 可以像 a ...
- MariaDB/MySQL用户和权限管理
本文目录: 1.权限验证 1.1 权限表 1.2 图解认证和权限分配的两个阶段 1.3 权限生效时机 2.用户管理 2.1 创建用户 2.2 create user和alter user 2.3 记录 ...
- 春天aopframework实现
Java的代码 包 cn.itcast.day3.aopframework; 进口 java.io.IOException的; 进口 的java.io.InputStream; 进口 java.u ...
- 【读书笔记】C++Primer---第二章
1.C++基本数据类型:字符型.整型.浮点型等: 2.算术类型如下表,下表只是表示C++要求的最小储存空间,但是一般编译器使用的都比所要求的储存空间大,以前类型所能表示的最大值也因机器的不同而有不同的 ...
- 关于.h .lib .dll的总结
对VC工程中的调用过程有些迷糊,所以就理清一下: 1.#include "...h"为头文件预编译命令,如果这些代码被修改,则需要重新编译生成预编译头文件. 预编译头的概念(转载) ...
- 使用nginx缓存服务器上的静态文件
一.nginx缓存的优点 如图所示,nginx缓存,可以在一定程度上,减少源服务器的处理请求压力. 因为静态文件(比如css,js, 图片)中,很多都是不经常更新的.nginx使用proxy_cach ...