栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Django uwsgi apscheduler定时任务重复执行问题解决

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Django uwsgi apscheduler定时任务重复执行问题解决

1、现状问题

为了满足用户动态添加定时任务,因此选择apscheduler模块,apscheduler支持cron指令形式的定时任务,可通过api接口实现动态添加定时任务。
问题:
django uwsig部署apscheduler定时任务重复执行问题,由于uwsgi启动多个worker,导致每个worker执行一次定时任务,各种google、baidu搜索结果都大差不差,自定义的定时任务可解决重复执行问题,但是动态添加定任务重复执行问题始终未能解决!

2、apscheduler部署过程
  • uwsgi.ini文件配置:(只列出关键的配置)
    #由于GIL的存在,uwsgi索性默认不支持多线程,不对GIL进行初始化。但如果希望程序中的线程发挥作用,需要加入
    enable-threads=true
  • -在django项目wsgi.py文件同级目录下:创建apscheduler_start.py文件

    该文件中是启动apscheduler定时任务的逻辑:(具体apscheduler使用方法这里不做说明)
    apscheduler_start.py:
import logging
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore

logger = logging.getLogger('scheduler_log')
logger.setLevel(logging.DEBUG)


scheduler = BackgroundScheduler()
# pool_pre_ping为True,即每次从连接池中取连接的时候,都会验证一下与数据库是否连接正常,如果没有连接,那么该连接会被回收。
# engine_options={'pool_pre_ping': True, 'pool_recycle': 100}
scheduler.add_jobstore(DjangoJobStore(), 'default')


def listener_event(event):
    if event.exception:
        logger.error(f'执行异常: {event.exception}')


scheduler._logger = logger
scheduler.add_listener(listener_event, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
try:
    # 此处每次先shutdown, 后开启, 自己猜测:由于apscheduler存在缓存机制,所以每次更新任务后,依然执行旧代码,导致任务不生效。 因此这里每次启动时先shutdown掉!!!
    scheduler.shutdown()
except:
    scheduler.start()

# 使用main_loop阻塞进程,防止进程挂起
# import time
# while True:
#     time.sleep(60)
#     sig = uwsgi.signal_wait()
  • 通过api请求动态添加定时任务的逻辑代码apscheduler_task.py:
# 引入apscheduler_start 中的 scheduler
from ops.apscheduler_start import scheduler
from django.views import View
from django.utils.decorators import method_decorator

class CrontabTask(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request, *args, **kwargs)
        return res

    def get(self, request):
        pass
    def post(self, request):
        content = json.loads(request.body.decode())
        try:
            # crontab_time 页面录入crontab指令,eg: */3 * * * * *
            job_time = content['crontab_time']
            crontab_time = job_time.split(' ')
            crontab_obj = ['minute', 'hour', 'day', 'month', 'day_of_week', 'year']
            crontab_dict = dict(zip(crontab_obj, crontab_time))
            # func需要定时执行的函数
            job = scheduler.add_job(func, trigger='cron', second='0', minute=crontab_dict['minute'],
                                    hour=crontab_dict['hour'], day=crontab_dict['day'], month=crontab_dict['month'],
                                    day_of_week=crontab_dict['day_of_week'],
                                    year=crontab_dict.get('year', '*'), args=[_job_id], coalesce=True, max_instances=1, misfire_grace_time=300, replace_existing=True)
        except Exception as e:
            logger.error(e)
        # 此处忽略return response代码
        
    def delete(self, request):
        pass
    def put(self, request):
        pass

此时通过api接口可成功添加定时任务, 但是会存在重复执行问题

3、解决重复执行问题

通过redis_lock锁实现

# 下载redis_lock
# pip install python-redis-lock
import redis_lock

# 使用锁的案列
# conn redis链接对象; lock-key:写入redis的key
lock = redis_lock.Lock(conn, "lock-key")
if lock.acquire(blocking=False):
    print("Got the lock.")
    # 此处为上述动态添加接口中:定时执行func的逻辑
    ...
    # 最后需要手动释放掉锁
    lock.release()
else:
    print("Someone else has the lock.")

4、apscheduler 跟随 uwsgi重启自动重启
  • 踩坑点:由于apscheduler需要手动触发重启,即在页面添加定时任务时,方可触发重启,导致定时任务执行的函数可能存在执行旧代码的情况!!! uwsgi重启时,apscheduler不会跟着重启!要保证uwsgi和apscheduler同步重启!
  • 解决方案:
    django项目中在apscheduler_start.py文件同级目录中__init__.py文件中添加:
import django
django.setup()
from ops.apscheduler_start import scheduler

__all__ = ('scheduler',)

此时重启uwsgi服务,apscheduler会自动重启。
**备注一下:**大部分是在wsgi.py文件中添加的,我尝试过,添加后定时任务会重启,但是django项目访问504,测试过多次,依然不行,不知道是不是方法不对。。。

5、较好的优化解决方案

通过类似celery的方式将django_apscheduler定时任务独立出来单独启动一套服务,设定单进程运行。这种方法解决了重复执行的问题,同时解放了django后台并发限制,并且可以处理即时的异步任务。(此处只有纯描述, 重要点:uwsgi中的mule模块!!!https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Mules.html)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/487271.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号