2017-04-23 47 views
3

我有一个功能需要在我的一个Web应用程序的后台运行。AppConfig.ready()在Django安装程序上运行两次(使用Heroku)

我实现了自定义的AppConfig,如下所示:

class MyAppConfig(AppConfig): 
    run_already = False 

    def ready(self): 
     from .tasks import update_products 
     if "manage.py" not in sys.argv and not self.run_already: 
      self.run_already = True 
      update_products() 

然而,当正在执行这个命令两次(在update_products()调用)

As stated in the documentation:

在通常的初始化过程中,准备好的方法一次只能由Django调用 。但在某些特殊情况下,特别是在安装了应用程序的测试中,准备好的可能会比一次更多地调用 。在这种情况下,要么写幂等方法,要么在你的AppConfig类中放置一个 标志,以防止重新运行的代码应该只执行一次。

我觉得我遵循文档所说的做。是什么赋予了?

+0

有同样的问题。你解决了吗? –

+1

@PavelBernshtam,如果我没有记错,它是运行多线程的gunicorn。当我更换服务员托管时,问题就消失了。我甚至没有包含'run_already = False','并且不包含self.run_already'代码。 – dmcmulle

回答

2

如在​​3210中所述,如果您正在运行应用程序,在Django上使用python manage.py runserver命令,您的应用程序将运行两次:一次验证模型,另一次运行您的应用程序。

您可以将此选项通过选项--noreload更改为runserver命令。

0

没有标志在课堂上有效。 Django在两个独立的进程上运行两次。两个独立进程上的类级变量彼此不可见。在这个代码中使用一个来自数据库表的标志(SchedulerUtils是我用go()方法编写的一个类,它启动了一个背景apscheduler调度器,该模型在表scheduler_schedulerinfo中使用了一行,所以你必须在这之前插入这一行: “INSERT INTO scheduler_schedulerinfo(启动)的值(0);”):

################################## APPS.PY 
import os 
from django.apps import AppConfig 
from apscheduler.schedulers.background import BlockingScheduler, BackgroundScheduler 
from scheduler.utils import SchedulerUtils 

class SchedulerConfig(AppConfig): 
    name = 'scheduler' 

    def ready(self): 
     startScheduler = True 
     pid = os.getpid() 

     #check i'm on heroku 
     if (os.environ.get("DYNO")): 
      # i'm on heroku, here runs twice 
      print("[%s] DYNO ENV exists, i'm on heroku" % pid) 
      from scheduler.models import SchedulerInfo 
      schedInfo = SchedulerInfo.objects.all().first() 
      if (schedInfo.started == 0): 
       print("[%s] Scheduler not started, starting.... " % pid) 
       startScheduler = True 
       # set flag to 1 
       SchedulerInfo.objects.all().update(started = 1) 
      else: 
       print("[%s] Scheduler already running, not starting." % pid) 
       startScheduler = False # already running 
       # reset to 0 for next time 
       SchedulerInfo.objects.all().update(started = 0) 

     # PRINT FLAG VALUE 
     from scheduler.models import SchedulerInfo 
     schedInfo = SchedulerInfo.objects.all().first() 
     print("[%s] Value of flag schedulerinfo.started: %d" % (pid, schedInfo.started)) 

     if (startScheduler): 
      su = SchedulerUtils() 
      su.go() 

##################################### MODELS.PY 
from django.db import models 

class SchedulerInfo(models.Model): 
    started = models.IntegerField(default=0) 
0

在Heroku,gunicorn开始与多于一个gunicorn工人。设置WEB_CONCURRENCY1

heroku config:set WEB_CONCURRENCY=1 

(见Basic configuration

0

另一种解决方案可以检查gunicorn的PID如下:

import os 
from django.apps import AppConfig 
import psutil 

class SchedulerConfig(AppConfig): 
    name = 'scheduler' 

    # I want to start ths scheduler only once, 
    # if WEB_CONCURRENCY is set and is greater than 1 
    # start the scheduler if the pid of this gunicorn is the same of the 
    # maximum pid of all gunicorn processes 
    def ready(self): 
     startScheduler = True 

     #check WEB_CONCURRENCY exists and is more than 1 
     web_concurrency = os.environ.get("WEB_CONCURRENCY") 
     if (web_concurrency): 
      mypid = os.getpid() 
      print("[%s] WEB_CONCURRENCY exists and is set to %s" % (mypid, web_concurrency)) 
      gunicorn_workers = int(web_concurrency) 
      if (gunicorn_workers > 1): 
       maxPid = self.getMaxRunningGunicornPid() 
       if (maxPid == mypid): 
        startScheduler = True 
       else: 
        startScheduler = False 

     if (startScheduler): 
      print("[%s] WILL START SCHEDULER", mypid) 
     else: 
      print("[%s] WILL NOT START SCHEDULER", mypid) 

    def getMaxRunningGunicornPid(self): 
     running_pids = psutil.pids() 
     maxPid = -1 
     for pid in running_pids: 
      proc = psutil.Process(pid) 
      proc_name = proc.name() 
      if (proc_name == "gunicorn"): 
       if (maxPid < pid): 
        maxPid = pid 
     print("Max Gunicorn PID: %s", maxPid) 
     return maxPid 
相关问题