V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fanne
V2EX  ›  Django

关于 django 使用 apscheduler 一次任务被重复执行问题

  •  
  •   fanne · 2018-01-15 17:17:43 +08:00 · 8832 次点击
    这是一个创建于 2502 天前的主题,其中的信息可能已经有所发展或是发生改变。

    APSwork 部分内容

    redis_connet = redis.Redis(host='localhost', port=6379, db=0)
    
    jobstores = {
        'redis': RedisJobStore(),
        }
    executors = {
        'default': ThreadPoolExecutor(4)
        }
    sched = BackgroundScheduler(jobstores=jobstores, executors=executors)
    
    def open_in(servername,serverip,cmd):
        print '{0}--open in ....'.format(servername)
        .....
    
    def open_out(servername,serverip,cmd):
        print '{0}---open out ....'.format(servername)
        .....
    
    def cronKaifu(starttime,servername,serverip,cmd_in,cmd_out):
        ....
    
        jobstores = {
            'redis': RedisJobStore(),
        }
        executors = {
            'default': ThreadPoolExecutor(1)
            # 'processpool': ProcessPoolExecutor(3)
        }
        sched = BackgroundScheduler(jobstores=jobstores, executors=executors)
    		sched.add_job(func=open_in,args=(servername,serverip,cmd_in,),trigger='date',next_run_time=open_in_time,jobstore='redis',id='{0}_in_{1}'.format(servername,sendtime_timestamp_in))
        print sched.get_jobs()
        
        
        sched.add_job(func=open_out,args=(servername,serverip,cmd_out,),trigger='date',next_run_time=open_out_time,jobstore='redis',id='{0}_out_{1}'.format(servername,sendtime_timestamp_out))
        print sched.get_jobs()
        sched.start()
    
    

    views.py 里面调用这个任务

    def kaifu_time(request):
    	....
    	....
    	cronKaifu(timeArray,server_name,server_ip,open_in_cmd,open_out_cmd)
    	......
    	....
    	return render(request, 'mt/kaifu_time.html', locals())
    

    添加任务后,两次打印 get_jobs()内容

    [<Job (id=mt_ios999game_in_1516005540000 name=open_in)>]
    [<Job (id=mt_ios999game_in_1516005540000 name=open_in)>, <Job (id=mt_ios999game_out_1516006260000 name=open_out)>]
    

    到点后,id=mt_ios999game_in_1516005540000,会被执行 2 次,有时候会是 3 次,但有时候又是正常的。

    [15/Jan/2018 15:02:22] "POST /mt/kaifu_time/ HTTP/1.1" 302 0
    [15/Jan/2018 15:02:22] "GET /mt/kaifu_time HTTP/1.1" 301 0
    [15/Jan/2018 15:02:22] "GET /mt/kaifu_time/ HTTP/1.1" 200 19539
    mt_ios999game--open in ....
    mt_ios999game--open in ....  <==这里的打印两次
    Exception in thread APScheduler:
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/threading.py", line 801, in __bootstrap_inner
        self.run()
      File "/usr/local/lib/python2.7/threading.py", line 754, in run
        self.__target(*self.__args, **self.__kwargs)
      File "/qzdata1/python_env/LazyOps/lib/python2.7/site-packages/apscheduler/schedulers/blocking.py", line 30, in _main_loop
        wait_seconds = self._process_jobs()
      File "/qzdata1/python_env/LazyOps/lib/python2.7/site-packages/apscheduler/schedulers/base.py", line 981, in _process_jobs
        self.remove_job(job.id, jobstore_alias)
      File "/qzdata1/python_env/LazyOps/lib/python2.7/site-packages/apscheduler/schedulers/base.py", line 613, in remove_job
        raise JobLookupError(job_id)
    JobLookupError: u'No job by the id of mt_ios999game_in_1516005540000 was found'
    

    我把两个 add_job()位置互掉后,即先添加 add_job(func=open_out...) 再添加 add_job(func=open_in..)

    然后 open_out 会被执行两次。

    这样的问题,我应如何修改。

    感觉毫无逻辑出现这问题,一次性任务,会被重复执行,而且是同一时间的。

    7 条回复    2019-12-25 02:47:26 +08:00
    oncew
        1
    oncew  
       2018-07-30 15:46:46 +08:00
    偶然看到这个问题,虽然很久了,但是回复一下可能的原因:你的程序运行的是 debug 模式,或者多进程,多线程模式时候,会有多个 scheduler 实例,这个时候会造成任务多次执行
    fanne
        2
    fanne  
    OP
       2018-07-30 15:58:17 +08:00
    @oncew #1 嗯,很大可能是的,后面遇到一个情况,有个 java 项目,因为某种原因起了两个进程,然后每日日报分析的时候,就多了一倍,然后我想到,我这个问题也有可能是类似问题造成的,不过当时没找到原因,就把这个功能撤了,就没有去测试。

    为了图简便,我的项目程序,直接 python manage.py runserver 丢后台跑
    然后有天我发现,我其实每次重启项目,这个进程都没停止掉就又起了一个,然后发现是有好多个进程的

    所以,很大可能是你描述的这个问题。
    oncew
        3
    oncew  
       2018-09-06 06:45:16 +08:00
    v2ex 的提醒功能做的不好,看其他消息提醒的时候,才注意这个帖子.. 重复执行的原因我能确定是多进程造成的问题,后面看了下 apschedler 的源码,对于多线程的情况是有加锁执行任务的,防止重复执行(希望没记错--),但是多进程的情况,这个线程锁没用,debug 模式(也会多起一个进程监听),后面我尝试加了一个多进程的锁,发现许多坑仍然没有解决,实现的功能也是 celery 已经早就解决的问题,所以我觉得用 celery 是最好的解决方案
    mingxu
        4
    mingxu  
       2018-09-11 10:42:03 +08:00
    相比 APScheduler,celery 不能实现动态添加任务的功能吧。这个问题还有其他的解决办法么?
    boyzhh
        5
    boyzhh  
       2019-11-12 04:01:03 +08:00
    我也遇到这个问题了,最简单的提交表格新建记录,经过反复测试,随机性的出现一个 view 同时执行两次,导致报错记录已存在,但因为执行了两次,新的记录也能存入,测试大概是 7-10 次就会出现一次同时执行两次的情况,莫名其妙,关闭了 debug 模式也一样
    fanne
        6
    fanne  
    OP
       2019-11-12 08:50:42 +08:00
    @boyzhh 后面有定位到哪问题么。
    boyzhh
        7
    boyzhh  
       2019-12-25 02:47:26 +08:00
    @fanne 恩,找到问题了,是 APPEND_SLASH=True 这个设置,就是连接结尾如果没有'/'自动加上,中间调试的时候打开这个的,也没注意,后来就间歇性出错,找了一通也没找到原因,后来努力回想改过什么配置,把这个去掉了就好了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3049 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 12:59 · PVG 20:59 · LAX 04:59 · JFK 07:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.