def excTasks(tasks): job_defaults = {'max_instances': 2} scheduler = BlockingScheduler(job_defaults=job_defaults) if tasks is not None: if isinstance(tasks[0], list): for task in tasks: addTask(scheduler, task) else: addTask(scheduler, tasks) scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) try: scheduler.start() except (KeyboardInterrupt, SystemExit): scheduler.pause()
def my_listener(event): if event.exception: print("保存出错信息") # 如果出现失误,首先将出错的消息发给服务器 # 日志记录下来 #1.直接关闭任务调度器 scheduler.resume() print("任务出错了!!!") else: print('任务照常进行...') if __name__ == '__main__': scheduler = BlockingScheduler() scheduler.add_job(job2, trigger='cron', second='*/5', args=['hello', 'world']) scheduler.add_job(job1, trigger='cron', second='*/5') #每天整点运行 # scheduler.add_job( # get_craw_data, # trigger='cron', # #hour='0-23', # minute='0-59', # ) scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) scheduler.start()
class Scheduler: def __init__(self): conf = configparser.ConfigParser() conf.read("../agent.ini") ip = conf.get("redis", "ip") port = conf.getint("redis", "port") timeout = conf.getint("redis", "timeout") self.invoker_id = self._get_invoker_id() self.max_tasks = conf.getint("invoker", "max_tasks") self.live_seconds = conf.getint("invoker", "live_seconds") self.db = SchedulerDb(ip, port, timeout) logging.config.fileConfig("../logger.ini") self.logger = logging.getLogger("main") executors = { 'default': {'type': 'processpool', 'max_workers': self.max_tasks + 1} } self.blockScheduler = BlockingScheduler() self.jobs = {} self.lock = threading.Lock() @staticmethod def _get_invoker_id(): hostname = socket.gethostname() pid = os.getpid() return hostname + "-" + str(pid) def task_invoke(self, task_instance, task_param): if task_param.cmd.startswith('http'): executor = HttpExecutor(self.db, task_instance, task_param) executor.execute() else: pass def break_heart(self): """ invoker每隔一段时间就心跳一下,看看是否有新任务,是否有任务需要更新 :param bs: :return: """ # 先看看参数是否有变化的把调度重启或者关闭 try: self.lock.acquire() self.refresh_local_invoker() self.refresh_other_invokers() if len(self.jobs) >= self.max_tasks: return task_instances, task_params = self.db.query_waiting_run_tasks(self.invoker_id, self.max_tasks - len(self.jobs), True) if len(task_instances) == 0: return for i in range(len(task_instances)): task_instance = task_instances[i] task_param = task_params[i] if task_instance.id not in self.jobs.keys(): self.logger.info("分配了新任务%s", task_instance.id) job = self.blockScheduler.add_job(self.task_invoke, next_run_time=( datetime.datetime.now() + datetime.timedelta(seconds=2)), args=[task_instance, task_param], id=task_instance.id) self.jobs[job.id] = job self.db.lock_invoker_instance(self.invoker_id, task_instance.id, self.live_seconds) else: self.logger.error("%s任务已经在运行", task_instance.id) finally: self.lock.release() def refresh_local_invoker(self): """ 调度的参数是否发生变化,如有需要重启调度 :param bs: :return: """ self.db.update_invoker_time(self.invoker_id, self.jobs.keys(), self.live_seconds) self.logger.info("%s心跳更新成功!", self.invoker_id) # 看看是否有需要停止的任务再自己这里,释放掉 stop_tasks = self.db.query_need_stop_tasks(self.invoker_id) for stop_task in stop_tasks: if stop_task in self.jobs.keys(): try: job = self.jobs[stop_task] task_instance = job.args[0] task_instance.status = 'off' job.pause() job.remove() except Exception as e: self.logger.error(e) self.jobs.pop(stop_task) try: self.blockScheduler.remove_job(stop_task) except Exception as e1: self.logger.error(e1) self.logger.info("人工停止了任务%s", stop_task) self.db.unlock_invoker_instance(self.invoker_id, stop_task, self.live_seconds) # 是否有参数变化的任务需要重启 c_jobs = copy.copy(self.jobs) for key in c_jobs.keys(): if key not in self.jobs.keys(): continue job = self.jobs[key] task_instance = job.args[0] old_task_param = job.args[1] # 判断参数是否发生变化,如果有变化重新执行任务 new_task_param = self.db.query_task_param(task_instance.task_param_id) # if new_task_param if not new_task_param.has_diff(old_task_param): continue try: task_instance.status = 'off' job.pause() job.remove() except Exception as e: self.logger.error(e) self.jobs.pop(key) try: self.blockScheduler.remove_job(key) except Exception as e1: self.logger.error(e1) self.logger.info("参数变化停止了任务%s", task_instance.id) self.db.unlock_invoker_instance(self.invoker_id, task_instance.id, self.live_seconds) self.db.add_task_waiting_run(task_instance.id) def refresh_other_invokers(self): """ 遍历所有的invoker,判断invoker是否超过存活期 :return: """ invokers = self.db.query_all_invokers() for invoker_id in invokers.keys(): if not self.db.invoker_is_live(self.invoker_id): task_instance_list = self.db.query_invoker_tasks(self.invoker_id) for task_instance_id in task_instance_list: self.db.add_task_waiting_run(task_instance_id) def main(self): try: self.db.register_invoker(self.invoker_id, self.max_tasks, self.live_seconds); self.blockScheduler.add_listener(self._job_listener, events.EVENT_JOB_ERROR | events.EVENT_JOB_MISSED) self.blockScheduler.add_job(self.break_heart, "interval", seconds=self.live_seconds / 2, id="break_heart") self.logger.info("开始启动调度...") self.blockScheduler.start() self.logger.info("启动调度成功!") except KeyboardInterrupt as e: self.logger.info(e) self.blockScheduler.shutdown() def _job_listener(self, ev): """ 监听job的事件,job完成后再发起下次调用,对于异常也要处理 :param ev: :return: """ if ev.code == events.EVENT_JOB_ERROR: self.logger.error(ev.exception) self.logger.error(ev.traceback) else: pass
class EventScheduler: def __init__(self, reporter: ResultReporter): self.reporter = reporter self.scheduler = BlockingScheduler() self.events = list() log_path = static_setting.settings["CaseRunner"].log_path log_file = os.path.join(log_path, "event_scheduler_log.log") self.log = logger.register("EventScheduler", filename=log_file, for_test=True) self.scheduler.add_listener(self._event_listen, EVENT_JOB_EXECUTED) def add_event(self, event, package, args, is_background, need_lock, start_time, interval=5, loop_count=1, description=""): m = importlib.import_module(package) event_cls = getattr(m, event) new_event = event_cls(description, log=self.log) new_event.need_lock = need_lock new_event.back_ground = is_background new_event.arguments = args new_event.interval = interval new_event.loop_count = loop_count # 生成一个STEP 的节点给Event操作 new_event.reporter = self.reporter.add_event_group(f"Event: {event}") if is_background: new_event.job = self.scheduler.add_job(new_event.run, "interval", seconds=interval, start_date=start_time, id=f"{event}{uuid.uuid4()}") else: new_event.job = self.scheduler.add_job(new_event.run, "date", run_date=start_time, id=f"{event}{uuid.uuid4()}") self.events.append(new_event) def remove_event(self, event_id): job = self.scheduler.get_job(event_id) if job: event_to_remove = None for event in self.events: if event.job == job: event_to_remove = event self.scheduler.remove_job(event_id) break if event_to_remove: self.events.remove(event_to_remove) def start(self): self.scheduler.start() def _event_listen(self, job): for event in self.events: if event.job.id == job.job_id: if event.back_ground: return else: if event.loop_count == 1: return delta = datetime.timedelta(seconds=event.interval) next_date = job.scheduled_run_time + delta event.job = self.scheduler.add_job( event.run, "date", run_date=next_date, id=f"{event.name}{uuid.uuid4()}") event.loop_count -= 1 return
class ActionScheduler: def __init__(self): self.scheduler = BlockingScheduler(jobstores=APSCHEDULER_SETTINGS['jobstores'], executors=APSCHEDULER_SETTINGS['executors'], job_defaults=APSCHEDULER_SETTINGS['job_defaults'], timezone=TIMEZONE_PST8PDT) pass def start(self): self._add_event_listener() # self._add_example_jobs() self._add_jobs() self.scheduler.start() def shutdown(self): # self.scheduler.remove_all_jobs() # save all jobs into sqlite, do not remove them self.scheduler.shutdown() def _add_event_listener(self): self.scheduler.add_listener(ActionScheduler.listener_jobs_status, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) self.scheduler.add_listener(ActionScheduler.listener_all_jobs_finished, EVENT_ALL_JOBS_REMOVED) # examples def _add_example_jobs(self): import datetime self.scheduler.add_job(func=ActionScheduler.job_example, args=["cron", ], trigger='cron', second='*/5', misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, id="cron") self.scheduler.add_job(func=ActionScheduler.job_example, args=["interval", ], trigger='interval', seconds=60, misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, id="interval") self.scheduler.add_job(func=ActionScheduler.job_example, args=["date", ], trigger='date', run_date=get_cur_time()+datetime.timedelta(seconds=12), id="date") # examples @staticmethod def job_example(job_type): print("job_example: {}".format(job_type)) def _add_jobs(self): # add reap alerts immediate job TODO test # self.scheduler.add_job(id="reap_alerts_immediate", func=ActionScheduler.job_reap_alerts_and_start_action_tasks, args=[], # misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, ) # add reap alerts interval job # self.scheduler.add_job(id="reap_alerts", func=ActionScheduler.job_reap_alerts_and_start_action_tasks, # args=[], trigger='interval', seconds=REAP_INTERVAL_SECONDS, # misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, ) # add gather & retry failed action tasks immediate job TODO test # self.scheduler.add_job(id="check_tasks_immediate", func=ActionScheduler.job_gather_and_retry_failed_action_tasks, args=[], # misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, ) # add gather & retry failed action tasks interval job # self.scheduler.add_job(id="check_tasks", func=ActionScheduler.job_gather_and_retry_failed_action_tasks, # args=[], trigger='interval', seconds=GATHER_FAILED_TASKS_INTERVAL_SECONDS, # misfire_grace_time=DEFAULT_MISFIRE_GRACE_TIME, replace_existing=True, ) pass @staticmethod def listener_all_jobs_finished(event): # this would hardly be invoked logger_.info('All jobs are done.') @staticmethod def listener_jobs_status(event): if event.exception: logger_.warn('Job {} crashed.'.format(event.job_id)) else: logger_.info('Job {} executed.'.format(event.job_id))
''' if isinstance(event, JobSubmissionEvent): print("任务{}触发执行".format(event.job_id)) if isinstance(event, JobExecutionEvent) and event.exception: print("任务{}抛出异常:{}".format(event.job_id, event.exception)) jobstores = { 'default': SQLAlchemyJobStore(url='sqlite:///db.sqlite3') #定时器任务持久化地址 } scheduler = BlockingScheduler(jobstores=jobstores) scheduler.add_job(func=aps_test, trigger=CronTrigger(hour=20, minute=48), replace_existing=True, id="sb") scheduler.add_job(func=aps_test, replace_existing=True, trigger=DateTrigger(run_date=datetime.datetime.now() + datetime.timedelta(seconds=12)), id="sbsb") scheduler.add_job(func=aps_test, # 任务函数 trigger=IntervalTrigger(seconds=5), # 执行计划 replace_existing=True, id="sbsbsb" # 唯一的 ) scheduler.add_listener(my_listener) scheduler.start()
class TaskExecutor: def __init__(self, db, task_instance, task_param): self.task_instance = task_instance self.task_param = task_param self.db = db # invoke log self.invoke_log_map = {} self.jobs = {} logging.config.fileConfig("../logger.ini") self.logger = logging.getLogger("taskExecutor") invoke_count = int(self.task_param.get_invoke_args()['invoke_count']) executors = { 'default': { 'type': 'threadpool', 'max_workers': invoke_count + 1 } } self.scheduler = BlockingScheduler(executors=executors) def execute(self): self.scheduler.add_listener( self._job_listener, events.EVENT_JOB_EXECUTED | events.EVENT_JOB_ERROR | events.EVENT_JOB_ADDED | events.EVENT_JOB_MISSED) # invoke_log_map up server self.scheduler.add_job(self._invoke_break_heart, "interval", seconds=2) try: self.scheduler.start() except Exception as e: print(e) self.scheduler.shutdown(wait=True) def _job_listener(self, ev): """ 监听job的事件,job完成后再发起下次调用,对于异常也要处理 :param ev: :return: """ if self.task_instance.status == 'off': return if ev.code == events.EVENT_JOB_ADDED: self.jobs[ev.job_id] = self.scheduler.get_job(ev.job_id) elif ev.code == events.EVENT_JOB_EXECUTED or ev.code == events.EVENT_JOB_ERROR: if ev.code == events.EVENT_JOB_ERROR: self.logger.error(ev.exception) self.logger.error(ev.traceback) job = self.jobs[ev.job_id] self.scheduler.add_job( job.func, next_run_time=(datetime.datetime.now() + datetime.timedelta(seconds=1)), id=ev.job_id, args=job.args) else: pass def _invoke_break_heart(self): if self.task_instance.status == 'off': jobs = self.scheduler.get_jobs() for job in jobs: try: job.pause() job.remove() except Exception as e: self.logger.error(e) self.db.save_task_logs(self.invoke_log_map)
parser.add_argument( '--log', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='WARNING', help="Log level") args = parser.parse_args() logging.basicConfig(level=getattr(logging, args.log)) config = configparser.ConfigParser() config.read_file(args.config_file) scheduler = BlockingScheduler() scheduler.add_listener( lambda event: logging.error("Exception in feed fetch:", exc_info=event.exception), EVENT_JOB_ERROR) interval = int(config['interval']['interval'], 10) agency_ids = [ key.split(':')[1] for key in config.keys() if key.startswith('agency:') ] for agency_id in agency_ids: for feed in config['agency:' + agency_id].items(): (feed_id, feed_url) = feed scheduler.add_job(get, 'interval', (agency_id, feed_id, feed_url, config['influxdb'], interval / 2), seconds=interval, id=agency_id + ":" + feed_id)
class TimerWork(): def __init__(self): self.jobs = [] self.sched = BlockingScheduler() self.sched.add_listener(self.listener, EVENT_JOB_ERROR | EVENT_JOB_EXECUTED) def listener(self, event): if event.exception: print 'JOB :<' else: print 'JOB :>' pass def exit(self): print 'shutdown now' self.sched.shutdown(wait=True) print 'shutdown ok' pass def pause(self): BaseScheduler.pause_job() pass def resume(self): BaseScheduler.remove_job() pass def add_job(self, func, gap_time=0, first_run_time=None, args=None, kwargs=None): ''' 添加定时任务 :param func: 执行函数 :param gap_time: 间隔时间,如果设置小于等于0的值忽略 单位s :param first_run_time: 第一次执行时间 datetime.datetime 如果不设置就按正常处理 :param args: 参数 :param kwargs: 参数 :return: ''' self.jobs.append(func) if type(first_run_time) == types.IntType: first_run_time = datetime.datetime.now() + datetime.timedelta(seconds=first_run_time) if gap_time > 0: if first_run_time: self.sched.add_job(func, 'interval', seconds=gap_time, next_run_time=first_run_time, args=args, kwargs=kwargs) else: self.sched.add_job(func, 'interval', seconds=gap_time, args=args, kwargs=kwargs) else: if first_run_time: self.sched.add_job(func, next_run_time=first_run_time, args=args, kwargs=kwargs) pass else: self.sched.add_job(func, args=args, kwargs=kwargs) pass pass def run(self): if len(self.jobs) > 0: self.sched.start() else: print 'no add jobs , so check'
class JobLauncher(object): def __init__(self, background=False, deamon=True, **kwargs): logging.basicConfig(format="[%(asctime)s] %(message)s", atefmt="%Y-%m-%d %H:%M:%S") logging.getLogger('apscheduler').setLevel(logging.DEBUG) if background: self.sched = BackgroundScheduler(deamon=deamon) # background else: self.sched = BlockingScheduler(deamon=deamon) # foreground # TODO: Read from configuration file. self.sched.configure( jobstores={ # "sqlite": SQLAlchemyJobStore(url='sqlite:///app/database/example.db'), # "default": MemoryJobStore() "default": SQLAlchemyJobStore(url='sqlite:///app/database/example.db') }, executors={ 'default': ThreadPoolExecutor(20), 'processpool': ProcessPoolExecutor(5) }, job_defaults={ 'coalesce': False, 'max_instances': 3 }, timezone=get_localzone() # Asia/Seoul ) self.retried = 0 self.logger = logging.getLogger('apscheduler') super(JobLauncher, self).__init__() def start(self): try: if self.sched.state != STATE_RUNNING: self.printJobs(jobstore='default') started = self.sched.start() except ConflictingIdError as e: traceback.print_exc() except KeyboardInterrupt as e: traceback.print_exc() finally: pass # Remove all remained store. # self.sched.remove_all_jobs() # for job in self.getJobs(): # if job.pending: # job.pause() self.logger.info('Finished') self.logger.info(self.getJobs()) self.printJobs() def stop(self, wait=False): if self.sched.state == STATE_RUNNING: self.sched.shutdown(wait=wait) def resume(self): if self.sched.state == STATE_RUNNING: self.sched.resume() def pause(self): if self.sched.state == STATE_RUNNING: self.sched.pause() def addListener(self, listener, types): self.sched.add_listener(listener, types) def addJob(self, job, **kwargs): execute, trigger, options = job.build(**kwargs) added_job = self.sched.add_job(execute, trigger, **options) self.printJobs() return added_job def getJob(self, job_id): return self.sched.get_job(job_id) def getJobs(self, jobstore=None): return self.sched.get_jobs(jobstore=jobstore) def removeJob(self, job_id, jobstore=None): return self.sched.remove_job(job_id, jobstore=jobstore) def removeAllJob(self, jobstore=None): return self.sched.remove_all_jobs(jobstore=jobstore) def printJobs(self, jobstore=None, out=None): return self.sched.print_jobs(jobstore=jobstore, out=None) def getJobState(self, job_id=None, jobstore=None): state = list() if job_id is not None: job = self.sched.get_job(job_id, jobstore=jobstore) if job is not None: temp = dict() temp[job.id] = { "next_run_time": job.next_run_time, "state": job.pending, } state.append(temp) else: for job in self.sched.get_jobs(jobstore=jobstore): temp = dict() temp[job.id] = { "next_run_time": job.next_run_time, "state": job.pending, } state.append(temp) return state