2017-04-23 74 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 
相關問題