2016-04-24 25 views
3

Ich verwende diesen Code als CherryPy Web Service sowohl unter Mac OS X als auch unter Ubuntu 14.04. Unter Verwendung von multiprocessing auf Python3 möchte ich die statische Methode worker() in einer asynchronen Weise innerhalb einer Process Pool starten.Multiprocessing apply_async() funktioniert nicht auf Ubuntu

Der gleiche Code läuft einwandfrei auf Mac OS X, in Ubuntu 14.04 worker() läuft nicht. I.e. durch das Debuggen des Codes innerhalb der POST Methode, die ich bin in der Lage zu sehen, dass jede Zeile ausgeführt wird - von

reqid = str(uuid.uuid4()) 

zu

return handle_error(202, "Request ID: " + reqid) 

Beginnend mit dem gleichen Code in Ubuntu 14.04, läuft es nicht die worker() Methode , nicht einmal eine print() an der Spitze der Methode (die protokolliert werden würde).

Hier ist der entsprechende Code (I weggelassen nur die handle_error()-Methode):

import cherrypy 
import json 
from lib import get_parameters, handle_error 
from multiprocessing import Pool 
import os 
from pymatbridge import Matlab 
import requests 
import shutil 
import uuid 
from xml.etree import ElementTree 

class Schedule(object): 
    exposed = True 

    def __init__(self, mlab_path, pool): 
     self.mlab_path = mlab_path 
     self.pool = pool 

    def POST(self, *paths, **params): 

     if validate(cherrypy.request.headers): 

      try: 
       reqid = str(uuid.uuid4()) 
       path = os.path.join("results", reqid) 
       os.makedirs(path) 
       wargs = [(self.mlab_path, reqid)] 
       self.pool.apply_async(Schedule.worker, wargs) 

       return handle_error(202, "Request ID: " + reqid) 
      except: 
       return handle_error(500, "Internal Server Error") 
     else: 
      return handle_error(401, "Unauthorized") 

    #### this is not executed #### 
    @staticmethod 
    def worker(args): 

     mlab_path, reqid = args 
     mlab = Matlab(executable=mlab_path) 
     mlab.start() 

     mlab.run_code("cd mlab") 
     mlab.run_code("sched") 
     a = mlab.get_variable("a") 

     mlab.stop() 

     return reqid 

    #### 

# to start the Web Service 
if __name__ == "__main__": 

    # start Web Service with some configuration 
    global_conf = { 
      "global": { 
          "server.environment": "production", 
          "engine.autoreload.on": True, 
          "engine.autoreload.frequency": 5, 
          "server.socket_host": "0.0.0.0", 
          "log.screen": False, 
          "log.access_file": "site.log", 
          "log.error_file": "site.log", 
          "server.socket_port": 8084 
         } 
    } 
    cherrypy.config.update(global_conf) 
    conf = { 
     "/": { 
      "request.dispatch": cherrypy.dispatch.MethodDispatcher(), 
      "tools.encode.debug": True, 
      "request.show_tracebacks": False 
     } 
    } 

    pool = Pool(3) 

    cherrypy.tree.mount(Schedule('matlab', pool), "/sched", conf) 

    # activate signal handler 
    if hasattr(cherrypy.engine, "signal_handler"): 
     cherrypy.engine.signal_handler.subscribe() 

    # start serving pages 
    cherrypy.engine.start() 
    cherrypy.engine.block() 
+0

Sie könnten versuchen, ein minimales reproduzierbares Beispiel zu liefern, das definitiv helfen wird. Auch "läuft nicht" ist ein wenig mehrdeutig ... erhalten Sie einen Fehler? Kannst du es posten? – Peque

+0

Hallo @Peque, ich habe keinen Fehler. Ich habe versucht, den Code zu debuggen, aber es scheint nicht ausgeführt zu werden - ich habe gerade mit einer einfachen 'print()' Anweisung begonnen, außerhalb der Methode wird die Ausgabe angezeigt. Ich habe ein minimal reproduzierbares Beispiel gegeben.Danke – gc5

Antwort

5

Ihre Logik versteckt das Problem vor Ihnen. Die apply_async-Methode gibt ein AsyncResult-Objekt zurück, das als Handler für die asynchrone Aufgabe dient, die Sie gerade geplant haben. Wenn Sie das Ergebnis der geplanten Aufgabe ignorieren, sieht das Ganze so aus, als würde es "stillschweigend versagen".

Wenn Sie versuchen, die Ergebnisse von dieser Aufgabe zu erhalten, würden Sie das eigentliche Problem sehen.

handler = self.pool.apply_async(Schedule.worker, wargs) 
handler.get() 

... traceback here ... 
cPickle.PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed 

Kurz gesagt, müssen Sie die Argumente, die Sie an den Pool passieren gewährleisten Picklable sind.

Instanz- und Klassenmethoden sind pickbar, wenn das Objekt/die Klasse, zu der sie gehören, ebenfalls einfügbar ist. Statische Methoden sind nicht auswählbar, da sie die Assoziation mit dem Objekt selbst verlieren. Daher kann die Beizbibliothek sie nicht korrekt serialisieren.

Als allgemeine Linie, ist es besser zu vermeiden, die Planung auf multiprocessing.Pool etwas anderes als eine oberste Ebene definierten Funktionen.

0

Ich löste das Verfahren @staticmethod-@classmethod ändern. Jetzt läuft der Job innerhalb der ProcessPool. Ich fand Klassenmethoden in diesem Fall nützlicher, wie erklärt here.

Danke.

+0

In diesem Fall ist es wichtig, dass es in der Klasse gebündelt ist, da jede Klasse in cherrypy einen Web Service darstellt. In meinem Fall muss jeder Web Service einen anderen Worker Thread ausführen, daher ist es besser, ihn in der entsprechenden Klasse gebündelt zu haben. Wenn es jedoch eine bessere Design-Praxis gibt, bitte sag es mir :) – gc5

+0

Ich denke, dass der Overhead des Erklärens von "Arbeiter" als eine "Klassenmethode" in diesem Fall vernachlässigbar ist, und der "Arbeiter" ist mit der Klasse verbunden genug um ein zu sein Klassenmitglied. Der Name 'worker' ist auf die Tatsache zurückzuführen, dass die Klasse bereits einen beschreibenden Namen dafür hat, was der Web Service tun wird, der' worker' arbeitet nur daran. Was sind die Vorteile einer statischen Methode? – gc5

2

Zum Ausführen von Hintergrundaufgaben mit Cherrypy ist es besser, wenn Sie einen asynchronen Aufgabenwarteschlangenmanager wie Celery oder RQ verwenden. Diese Dienste sind sehr einfach zu installieren und auszuführen, Ihre Aufgaben werden in einem vollständig getrennten Prozess ausgeführt und wenn Sie skalieren müssen, weil Ihre Last zunimmt, wird es sehr einfach sein.

Sie haben ein einfaches Beispiel mit Cherrypy here.