关于django apscheduler DjangoJobStore

apscheduler (环境 Python 2.7 django 1.11)

背景

  • 实现项目中的动态任务和定时任务

使用场景

  • 业务设计到支付 在下单后发送一个Signal 去添加一个动态任务 如果支付成功过后 通过取消Signal删掉该任务 如果未支付的状态下时间到了执行函数自动取消订单

使用 DjangoJobStore 出现的问题

  • 先看看DjangoJobStore的开启方法

    from apscheduler.schedulers.background import BackgroundScheduler
    from django_apscheduler.jobstores import register_events, DjangoJobStore
    
    django_scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
    django_scheduler.add_jobstore(DjangoJobStore(), "default")
    
    register_events(django_scheduler)
    
  • 通过add_job方法添加任务

    # 这里由于涉及到订单操作id使用到了app_label, model_name 以及实例id 这里通过信号传递进来
    django_scheduler.add_job(func,  # 取消订单的逻辑函数
                             trigger='date',
                             run_date=run_date,
                             args=(app_label, model_name, instance.id),
                             id='%s.%s.%s' % (app_label, model_name, instance.id))
    
  • 上述步骤完成了动态任务的操作 看似已经天衣无缝的操作 实际上隐藏了一个巨大的坑在里面

    • 先剖析一下DjangoJobStore add_job的源码
       @ignore_database_error()
        def add_job(self, job):
            dbJob, created = DjangoJob.objects.get_or_create(
                defaults=dict(
                    next_run_time=serialize_dt(job.next_run_time),
                    job_state=pickle.dumps(job.__getstate__(), self.pickle_protocol)
                ),
                name=job.id,
            )
    
            if not created:
                LOGGER.warning("Job with id %s already in jobstore. I'll refresh it", job.id)
                dbJob.next_run_time = serialize_dt(job.next_run_time)
                dbJob.job_state=pickle.dumps(job.__getstate__(), self.pickle_protocol)
                dbJob.save()
    
  • 上述源码主要是通过DjangoJob中的add_job来添加一个任务 看起来也就是通过name来确定是否新增任务但是函数头上是有个 ignore_database_error 装饰器的 下面我们看看它的源码

    def ignore_database_error(on_error_value=None):
    
        def dec(func):
            from functools import wraps
    
            @wraps(func)
            def inner(*a, **k):
                try:
                    return func(*a, **k)
                except (OperationalError, ProgrammingError) as e:
                    warnings.warn(
                        "Got OperationalError: {}. "
                        "Please, check that you have migrated the database via python manage.py migrate".format(e),
                        category=RuntimeWarning,
                        stacklevel=3
                    )
                    return on_error_value
                finally:
                    db.connections.close_all()
            return inner
        return dec
    
    • 通过上面的代码我们很容易发现不管是新增还是更新任务都会将数据库的连接关闭, 如果此时还有其它数据库的IO操作那么将都会是不成功的, 不过由于是Python2已经停止更新了这个我也没有找到比较好的解决方法, 后面决定上了RedisJobStore 通过redis去做了这个事情, 我去看了更新后Python3去实现的这个add_job方法作者还是做了一些改变 第二张图是装饰器的代码, 只有当捕捉到db.OperationalError, db.InterfaceError 才会关闭数据库的连接, 看起来是可行的 由于使用Python2就没有再去尝试了
    • 关于django apscheduler DjangoJobStore_第1张图片
      关于django apscheduler DjangoJobStore_第2张图片

Hope

  • -----------欢迎大家提出自己的看法 如果有什么错误之处还望有高手指出 共同商讨----------

  • django-apscheduler 地址 大家有兴趣可以去看看

你可能感兴趣的:(django,django,python,数据库)