2017-04-23 30 views
3

私のWebアプリケーションの1つでバックグラウンドで実行する必要がある機能があります。AppConfig.ready()がDjangoセットアップで2回実行しています(Herokuを使用)

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:

通常の初期化では2回実行されている。以下に示すように

私はカスタムのAppConfigを実装しましたプロセスでは、readyメソッドはDjangoによって初めて と呼ばれます。しかし、いくつかのコーナーケースでは、特に がインストールされたアプリケーションを操作しているテストでは、readyはより多く と呼ばれるかもしれません。その場合は、冪等のメソッドを書くか、 フラグをAppConfigクラスに入れて、 を正確に1回実行するコードを再実行しないようにします。

ドキュメントに記載されていることに従っているように感じます。何がありますか?

+0

同じ問題を抱えています。あなたはそれを解決しましたか? –

+1

@PavelBernshtam、私が正しく覚えていれば、それは複数のスレッドを実行しているgunicornでした。私がウェイトレスにホスティングするように変更したとき、問題はなくなった。私は 'run_already = False'、 'not self.run_already'のコードも含めなかった。 – dmcmulle

答えて

2

​​3210で述べたように、あなたのアプリケーションを実行している場合、Djangoでpython manage.py runserverコマンドを使用すると、アプリケーションは2回実行されます:モデルの検証に1回、アプリケーションを実行するもう1回です。

これを変更するには、--noreloadオプションをrunserverコマンドに渡します。

0

フラグはクラスレベルで機能しません。 Djangoは2つの別々のプロセスで2回実行されます。 2つの別々のプロセス上のクラスレベルの変数は、互いに見えません。このコードのように、データベーステーブルのフラグを使用してください(SchedulerUtilsはbackgroud apschedulerスケジューラを起動するメソッドgo()で私が書いたクラスです)。モデルはscheduler_schedulerinfoテーブルの行を使用するので、 "scheduler_schedulerinfoのINSERT INTO(開始)値は(0);"):

Herokuの上
################################## 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

、gunicornは、複数のgunicornワーカーで開始されます。 1WEB_CONCURRENCYを設定します。

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 
関連する問題