python - django,fastcgi:如何管理長時間運行的進程?



(2)

我繼承了一個需要修改的django + fastcgi應用程序來執行冗長的計算(長達半小時或更長時間)。 我想要做的是在後台運行計算,並返回“你的工作已經開始”類型的響應。 當進程正在運行時,進一步命中該url應該返回“你的工作仍在運行”,直到工作完成,在這一點上應該返回工作的結果。 任何後續的網址應該會返回緩存的結果。

我在django是一個完全的新手,十年沒有做任何重要的網絡工作,所以我不知道是否有一個內置的方式來做我想做的事情。 我已經嘗試通過subprocess.Popen()開始進程,並且工作正常,除了它在進程表中留下一個不存在的條目。 我需要一個乾淨的解決方案,可以刪除臨時文件和完成後的任何過程的痕跡。

我也嘗試過fork()和線程,還沒有拿出一個可行的解決方案。 有沒有一個規範的解決方案,似乎我是一個相當常見的用例? FWIW這只會用在流量很低的內部服務器上。

https://ffff65535.com


也許你可以反過來看看這個問題。

也許你可以嘗試DjangoQueueService ,並且有一個監聽隊列的“守護進程”,看看是否有新的東西並處理它。


我現在必須解決類似的問題。 它不會成為一個公共站點,但同樣也是一個低流量的內部服務器。

技術約束:

  • 所有輸入數據到長時間運行的過程都可以在其開始時提供
  • 長時間運行的過程不需要用戶交互(除了初始輸入來啟動一個過程)
  • 計算時間足夠長,以至於不能立即向HTTP客戶端返回結果
  • 需要從長期運行的過程中得到某種反饋(排序進度條)。

因此,我們至少需要兩個網絡“觀點”:一個是啟動長期運行的過程,另一個是監視其狀態/收集結果。

我們還需要某種進程間通信:從發起者 (http請求上的web服務器)向長時間運行的進程發送用戶數據,然後將結果發送給接收者 (再次web服務器,由http請求驅動)。 前者容易,後者不太明顯。 與正常的unix編程不同,接收器最初並不知道。 接收者可以是與發起者不同的進程,並且可以在長時間運行的作業仍在進行或已經完成時開始。 所以管道不工作,我們需要長時間運行的結果的一些永恆。

我看到兩個可能的解決方案

  • 對長時間運行的工作管理器(這可能是上述的django-queue-service是什麼)發送長時間運行的進程;
  • 將結果永久保存在文件或數據庫中。

我更喜歡使用臨時文件並記住會話數據中的位置。 我不認為這可以變得更簡單。

作業腳本(這是長時間運行的過程) myjob.py

import sys
from time import sleep

i = 0
while i < 1000:
    print 'myjob:', i  
    i=i+1
    sleep(0.1)
    sys.stdout.flush()

django urls.py映射:

urlpatterns = patterns('',
(r'^startjob/$', 'mysite.myapp.views.startjob'),
(r'^showjob/$',  'mysite.myapp.views.showjob'),
(r'^rmjob/$',    'mysite.myapp.views.rmjob'),
)

django的觀點:

from tempfile import mkstemp
from os import fdopen,unlink,kill
from subprocess import Popen
import signal

def startjob(request):
     """Start a new long running process unless already started."""
     if not request.session.has_key('job'):
          # create a temporary file to save the resuls
          outfd,outname=mkstemp()
          request.session['jobfile']=outname
          outfile=fdopen(outfd,'a+')
          proc=Popen("python myjob.py",shell=True,stdout=outfile)
          # remember pid to terminate the job later
          request.session['job']=proc.pid
     return HttpResponse('A <a href="/showjob/">new job</a> has started.')

def showjob(request):
     """Show the last result of the running job."""
     if not request.session.has_key('job'):
          return HttpResponse('Not running a job.'+\
               '<a href="/startjob/">Start a new one?</a>')
     else:
          filename=request.session['jobfile']
          results=open(filename)
          lines=results.readlines()
          try:
               return HttpResponse(lines[-1]+\
                         '<p><a href="/rmjob/">Terminate?</a>')
          except:
               return HttpResponse('No results yet.'+\
                         '<p><a href="/rmjob/">Terminate?</a>')
     return response

def rmjob(request):
     """Terminate the runining job."""
     if request.session.has_key('job'):
          job=request.session['job']
          filename=request.session['jobfile']
          try:
               kill(job,signal.SIGKILL) # unix only
               unlink(filename)
          except OSError, e:
               pass # probably the job has finished already
          del request.session['job']
          del request.session['jobfile']
     return HttpResponseRedirect('/startjob/') # start a new one




fastcgi