在所有mod_wsgi进程之间的Flask / mod_wsgi应用程序中共享全局数据

时间:2014-02-19 17:12:43

标签: python multithreading apache flask mod-wsgi

代码说明:

我正在尝试从我的Flask网络应用程序启动和管理一些流程。我希望能够启动一个进程并能够终止/终止它。

在我的例子中,testscript.sh只是睡了10秒钟。

我的类processManager实现了Borg设计模式,试图解决我的问题,但它没有改变任何东西。 (事先,我的进程,锁和线程变量只是全局变量,而processManager根本就不是一个类。)

processes属性是所有已启动进程的列表。

addProcess启动一个新进程,将其添加到进程。它查看轮询线程是否正在运行,如果不是则启动它。

轮询线程轮询进程列表中的所有进程,并检查其退出状态是否已更改。如果所有进程都完成,则线程停止。

stopProcess循环遍历进程以找到正确的进程并终止它。

class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state
# Pokes the processes to see their exit status. Stops when there is no running thread left.
class processManager(Borg):
class pollProcesses (threading.Thread):
    def __init__(self, pManager):
        threading.Thread.__init__(self)
        self.pManager = pManager
    def run(self):
        while True:
            #poll exit codes:
            #None   Running
            #0      Finished nicely 
            #-n     terminated by signal n
            time.sleep(1)
            pprint.pprint("-----------")
            self.pManager.lock.acquire()
            stop = True
            for p in self.pManager.processes:
                status = p.poll()
                pprint.pprint(str(p.jobid)+" "+str(p.pid)+" "+str(status))
                if status is None:
                    stop = False
                                    #else log process status somewhere

            self.pManager.lock.release()
            if stop:
                pprint.pprint("-----------")
                pprint.pprint("No process running, stopping scan.")
                break;

def __init__(self):
    Borg.__init__(self)
    pprint.pprint("New instance!")
    if not hasattr(self, "processes"):
        pprint.pprint("FIRST instance!")
        self.processes = []
    if not hasattr(self, "lock"):
        self.lock = threading.Lock()
    if not hasattr(self, "thread"):
        self.thread = None

def addProcess(self, job):
    pprint.pprint("-----------")
    path = os.path.realpath(__file__)

    pprint.pprint("Adding new process")
    p = Popen(["/path/to/testscript.sh"], shell=True)
    p.jobid = job['id']

    # Lock the processes list before adding data
    self.lock.acquire()
    self.processes.append(p)

    #If thread is finished, start it up
    if self.thread is None or not self.thread.isAlive():
        pprint.pprint("Starting thread")
        self.thread = None
        self.thread = self.pollProcesses(self)
        self.thread.start()
        pprint.pprint(self.thread)
    self.lock.release()
    return

def stopProcess(self, jobid):
    pprint.pprint("STOP job"+str(jobid))
    self.lock.acquire()
    pprint.pprint(self.thread)
    pprint.pprint("Jobs in processes:")
    for p in self.processes:
        pprint.pprint(p.jobid)
        if p.jobid == jobid:
            p.terminate()
    self.lock.release()
    return

在烧瓶应用程序中,我基本上做了这样的事情:

@plugin.route('/startjob', methods=['GET'])
def startJob():
    if not hasattr(g, "pManager"):
        g.pManager = processManager()

    #SNIP - create/obtain job

    g.pManager.addProcess(job)
    return "OK"

与停止工作相似的东西。

现在,如果我启动一项工作并尝试从我的Flask应用程序停止它,有时进程列表将创建一个全新的Borg / processManager(打印“FIRST实例!”)

从那时起,一切都变得无法预测:

即使来自两个线程和两个不同的进程列表,我的进程状态仍在更新。这不是我的目标,但它确实有效。

我的问题:

但是如果我想停止一个进程,stopProcess函数可能是“错误的”,因为现在有几个具有不同进程列表的processManager,我无法知道被调用的stopProcess函数是否有访问权限。我希望停止特定的过程。

我认为这可能是由mod_wsgi或apache引起的,其多线程/多处理机制导致我的processManager在完全不同的上下文中运行。

我希望processManager的进程列表,锁和线程是相同的,无论mod_wsgi进程正在访问它。

我仍然是python的新手,所以我的问题可能完全不同。任何帮助表示赞赏。

请记住,这是简化的代码,它仍然会重现问题,但它确实没有做太多。

1 个答案:

答案 0 :(得分:2)

通常,从Web应用程序创建子进程不是一个好主意,因为它可能会导致各种问题,具体取决于所使用的Web服务器。由于使用多进程Web服务器配置,从父进程继承奇数信号掩码等问题,可能会出现问题。

如果您想创建作业来执行某些任务,那么使用专门构建的任务排队系统(例如Celery,Redis Queue(RQ),gearman或类似工具)可能会更好。