189 lines
7.3 KiB
Python
189 lines
7.3 KiB
Python
import traceback
|
|
from pytz import timezone
|
|
import sys
|
|
import threading
|
|
from datetime import datetime, timedelta
|
|
from random import randint
|
|
import time
|
|
|
|
# third-party
|
|
from apscheduler.jobstores.base import JobLookupError
|
|
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
|
|
|
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
|
|
from apscheduler.triggers.cron import CronTrigger
|
|
|
|
jobstores = {"default": SQLAlchemyJobStore(url="sqlite:///data/db/gommi.db")}
|
|
|
|
excutors = {"default": ThreadPoolExecutor(20)}
|
|
|
|
job_defaults = {"coalesce": False, "max_instances": 1}
|
|
|
|
# gommi 공용
|
|
from framework.logger import get_logger
|
|
|
|
# 패키지
|
|
# ==========================================================================
|
|
package_name = __name__.split(".")[0]
|
|
logger = get_logger(package_name)
|
|
|
|
|
|
class Scheduler(object):
|
|
job_list = []
|
|
first_run_check_thread = None
|
|
|
|
def __init__(self, args):
|
|
# (jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
|
|
# self.sched = GeventScheduler( executors=executors)
|
|
try:
|
|
if args is None or (args is not None and args.use_gevent):
|
|
from apscheduler.schedulers.gevent import GeventScheduler
|
|
|
|
self.sched = GeventScheduler(timezone="Asia/Seoul")
|
|
else:
|
|
raise Exception("")
|
|
except Exception:
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
|
|
self.sched = BackgroundScheduler(timezone="Asia/Seoul")
|
|
# jobstores=jobstores, 에러 pickle 직렬화 할수없다
|
|
# self.sched.configure(executors=executors, job_defaults=job_defaults, timezone='Asia/Seoul')
|
|
# self.sched.configure(executors=executors, timezone='Asia/Seoul')
|
|
self.sched.start()
|
|
logger.debug("SCHEDULER...")
|
|
|
|
def first_run_check_thread_function(self):
|
|
logger.warning("XX first_run_check_thread_function")
|
|
|
|
try:
|
|
# time.sleep(60)
|
|
# for i in range(5):
|
|
flag_exit = True
|
|
for job_instance in self.job_list:
|
|
if not job_instance.run:
|
|
continue
|
|
if (
|
|
job_instance.count == 0
|
|
and not job_instance.is_running
|
|
and job_instance.is_interval
|
|
):
|
|
# if job_instance.count == 0 and not job_instance.is_running:
|
|
job = self.sched.get_job(job_instance.job_id)
|
|
if job is not None:
|
|
logger.warning("job_instance : %s", job_instance.plugin)
|
|
logger.warning("XX job re-sched:%s", job)
|
|
flag_exit = False
|
|
tmp = randint(1, 20)
|
|
job.modify(
|
|
next_run_time=datetime.now(timezone("Asia/Seoul"))
|
|
+ timedelta(seconds=tmp)
|
|
)
|
|
# break
|
|
else:
|
|
pass
|
|
if flag_exit:
|
|
self.remove_job("scheduler_check")
|
|
# time.sleep(30)
|
|
logger.warning("first_run_check_thread_function end!!")
|
|
except Exception as exception:
|
|
logger.error("Exception:%s", exception)
|
|
logger.error(traceback.format_exc())
|
|
|
|
def get_job_list_info(self):
|
|
|
|
ret = []
|
|
idx = 0
|
|
job_list = self.sched.get_jobs()
|
|
# logger.debug('len jobs %s %s', len(jobs), len(Scheduler.job_list))
|
|
for j in job_list:
|
|
idx += 1
|
|
entity = {}
|
|
entity["no"] = idx
|
|
entity["id"] = j.id
|
|
entity["next_run_time"] = j.next_run_time.strftime("%m-%d %H:%M:%S")
|
|
remain = j.next_run_time - datetime.now(timezone("Asia/Seoul"))
|
|
tmp = ""
|
|
if remain.days > 0:
|
|
tmp += "%s일 " % (remain.days)
|
|
remain = remain.seconds
|
|
if remain // 3600 > 0:
|
|
tmp += "%s시간 " % (remain // 3600)
|
|
remain = remain % 3600
|
|
if remain // 60 > 0:
|
|
tmp += "%s분 " % (remain // 60)
|
|
tmp += "%s초" % (remain % 60)
|
|
# entity['remain_time'] = (j.next_run_time - datetime.now(timezone('Asia/Seoul'))).seconds
|
|
entity["remain_time"] = tmp
|
|
job = self.get_job_instance(j.id)
|
|
if job is not None:
|
|
entity["count"] = job.count
|
|
entity["plugin"] = job.plugin
|
|
if job.is_cron:
|
|
entity["interval"] = job.interval
|
|
elif job.interval == 9999:
|
|
entity["interval"] = "항상 실행"
|
|
entity["remain_time"] = ""
|
|
else:
|
|
entity["interval"] = "%s분 %s초" % (
|
|
job.interval,
|
|
job.interval_seconds,
|
|
)
|
|
entity["is_running"] = job.is_running
|
|
entity["description"] = job.description
|
|
entity["running_timedelta"] = (
|
|
job.running_timedelta.seconds
|
|
if job.running_timedelta is not None
|
|
else "-"
|
|
)
|
|
entity["make_time"] = job.make_time.strftime("%m-%d %H:%M:%S")
|
|
entity["run"] = job.run
|
|
else:
|
|
entity["count"] = ""
|
|
entity["plugin"] = ""
|
|
entity["interval"] = ""
|
|
entity["is_running"] = ""
|
|
entity["description"] = ""
|
|
entity["running_timedelta"] = ""
|
|
entity["make_time"] = ""
|
|
entity["run"] = True
|
|
|
|
ret.append(entity)
|
|
return ret
|
|
|
|
def add_job_instance(self, job_instance, run=True):
|
|
from framework import app
|
|
|
|
if app.config["config"]["run_by_real"] and app.config["config"]["auth_status"]:
|
|
if not self.is_include(job_instance.job_id):
|
|
job_instance.run = run
|
|
Scheduler.job_list.append(job_instance)
|
|
if job_instance.is_interval:
|
|
self.sched.add_job(
|
|
job_instance.job_function,
|
|
"interval",
|
|
minutes=job_instance.interval,
|
|
seconds=job_instance.interval_seconds,
|
|
id=job_instance.job_id,
|
|
args=(None),
|
|
)
|
|
elif job_instance.is_cron:
|
|
self.sched.add_job(
|
|
job_instance.job_function,
|
|
CronTrigger.from_crontab(job_instance.interval),
|
|
id=job_instance.job_id,
|
|
args=(None),
|
|
)
|
|
# self.sched.add_job(job_instance.job_function, 'interval', minutes=job_instance.interval,
|
|
# id=job_instance.job_id, args=(None))
|
|
job = self.sched.get_job(job_instance.job_id)
|
|
if run and job_instance.is_interval:
|
|
tmp = randint(5, 20)
|
|
job.modify(
|
|
next_run_time=datetime.now(timezone("Asia/Seoul"))
|
|
+ timedelta(seconds=tmp)
|
|
)
|
|
|
|
def is_include(self, job_id):
|
|
job = self.sched.get_job(job_id)
|
|
return job is not None
|