2014-06-27 3 views
5

Ich versuche mit Pyro eine Slave-Maschine zu steuern. Ich rsync die notwendigen Python-Dateien, starten Sie einen Pyro-Server, führen Sie einige Aktionen per Fernsteuerung, und dann möchte ich den Pyro-Server zum Herunterfahren sagen.Wie kann ich einen Pyro Daemon sauber durch eine Client-Anfrage verlassen?

Ich habe Probleme, den Pryo Daemon sauber herunterzufahren. Entweder hängt es im Daemon.close() Aufruf, oder wenn ich diese Zeile auskommentiere, ohne den Socket korrekt herunterzufahren, was zu socket.error: [Errno 98] Address already in use führt, wenn ich den Server zu früh neu starte.

Es ist nicht der Meinung, dass SO_REUSEADDR die richtige Lösung ist, da ein unsauberes Herunterfahren des Sockels immer noch dazu führt, dass ein Socket im Zustand TIME_WAIT herumhängt und möglicherweise einige Clients Probleme verursacht. Ich denke, die bessere Lösung ist, den Pyro-Dämon zu überzeugen, seinen Sockel richtig zu schließen.

Ist es unangemessen, Daemon.shutdown() aus dem Daemon heraus aufzurufen?

Wenn ich einen Server starte und dann CTRL-C ohne angeschlossene Clients drücke, habe ich keine Probleme (keine Address already in use Fehler). Das macht ein sauberes Herunterfahren möglich, meistens (unter der Annahme eines ansonsten vernünftigen Clients und Servers).

Beispiel: server.py

import Pyro4 

class TestAPI: 
    def __init__(self, daemon): 
     self.daemon = daemon 
    def hello(self, msg): 
     print 'client said {}'.format(msg) 
     return 'hola' 
    def shutdown(self): 
     print 'shutting down...' 
     self.daemon.shutdown() 

if __name__ == '__main__': 
    daemon = Pyro4.Daemon(port=9999) 
    tapi = TestAPI(daemon) 
    uri = daemon.register(tapi, objectId='TestAPI') 
    daemon.requestLoop() 
    print 'exited requestLoop' 
    daemon.close() # this hangs 
    print 'daemon closed' 

Beispiel: client.py

import Pyro4 

if __name__ == '__main__': 
     uri = 'PYRO:[email protected]:9999' 
     remote = Pyro4.Proxy(uri) 
     response = remote.hello('hello') 
     print 'server said {}'.format(response) 
     try: 
      remote.shutdown() 
     except Pyro4.errors.ConnectionClosedError: 
      pass 
     print 'client exiting' 
+0

Hey Eric. Ich hatte nie die 'Adresse 'bereits für den Pyro-Server benutzt, aber ich bekomme die ganze Zeit für den' Nameserver'. Wenn ich STRG + C auf dem NameServer trage, besteht eine Chance von 50%, diesen Fehler zu verursachen, wenn ich den Nameserver innerhalb von 30 Sekunden erneut ausführe. Hattest du das schon mal? –

Antwort

0

Ich glaube, ich bin in der Nähe zu einer Lösung: eine Kombination mit dem loopCondition Parameter requestloop() und den Config-Wert COMMTIMEOUT.

server.py

import Pyro4 
Pyro4.config.COMMTIMEOUT = 1.0 # without this daemon.close() hangs 

class TestAPI: 
    def __init__(self, daemon): 
     self.daemon = daemon 
     self.running = True 
    def hello(self, msg): 
     print 'client said {}'.format(msg) 
     return 'hola' 
    def shutdown(self): 
     print 'shutting down...' 
     self.running = False 

if __name__ == '__main__': 
    daemon = Pyro4.Daemon(port=9999) 
    tapi = TestAPI(daemon) 
    uri = daemon.register(tapi, objectId='TestAPI') 
    def checkshutdown(): 
     return tapi.running 
    daemon.requestLoop(loopCondition=checkshutdown) # permits self-shutdown 
    print 'exited requestLoop' 
    daemon.close() 
    print 'daemon closed' 

Leider gibt es einen Zustand, in dem es noch eine Steckdose hinter im TIME_WAIT-Zustand verlässt. Wenn der Client seinen Socket nach dem Server schließt, gibt der nächste Versuch, den Server zu starten, denselben Address already in use Fehler zurück.

Der einzige Weg, die ich finden kann, dies zu umgehen ist, den Server COMMTIMEOUT länger zu machen (oder Schlaf für einige Sekunden vor daemon.close() Aufruf), und stellen Sie sicher, dass der Client ruft immer _pyroRelease() direkt nach dem Shutdown:

client.py

import Pyro4 

if __name__ == '__main__': 
     uri = 'PYRO:[email protected]:9999' 
     remote = Pyro4.Proxy(uri) 
     response = remote.hello('hello') 
     print 'server said {}'.format(response) 
     remote.shutdown() 
     remote._pyroRelease() 
     print 'client exiting' 

ich nehme an, dass ist gut genug, aber die Ungerechtigkeit der Terminplanung und Netzwerkverzögerungen ist es nach wie vor enttäuschend zu haben, dass race-Bedingung gegeben lauert.

+0

Ich habe beim Testen festgestellt, dass die Verwendung von COMMTIMEOUT zu aggressiv zu Fehlfehlern führt, also musste ich dies auf 5 Sekunden abstellen. Ein weiterer Grund, warum sich diese Lösung nicht genau richtig anfühlt. –

2

Ich denke, das kann ohne Verwendung von Timeout oder LoopCondition getan werden, indem Sie Ihre shutdown() rufen Sie den Daemon shutdown. Nach http://pythonhosted.org/Pyro4/servercode.html#cleaning-up:

Eine weitere Möglichkeit Pyro4.core.Daemon.shutdown() auf dem laufenden bdaemon Objekt aufruft. Dies wird auch aus der Anforderungsschleife ausbrechen und ermöglicht es Ihrem Code, sauber hinter sich selbst aufzuräumen, und wird auch ohne weitere Anforderungen auf dem Server mit Threads arbeiten.

Folgendes funktioniert auf Python3.4.2 unter Windows. Der @Pyro4.oneway Dekorateur für shutdown wird hier nicht benötigt, ist es aber in manchen Situationen.

server.py

import Pyro4 
# using Python3.4.2 

@Pyro4.expose 
class TestAPI: 
    def __init__(self, daemon): 
     self.daemon = daemon 
    def hello(self, msg): 
     print('client said {}'.format(msg)) 
     return 'hola' 
    @Pyro4.oneway # in case call returns much later than daemon.shutdown 
    def shutdown(self): 
     print('shutting down...') 
     self.daemon.shutdown() 

if __name__ == '__main__': 
    daemon = Pyro4.Daemon(port=9999) 
    tapi = TestAPI(daemon) 
    uri = daemon.register(tapi, objectId='TestAPI') 
    daemon.requestLoop() 
    print('exited requestLoop') 
    daemon.close() 
    print('daemon closed') 

client.py

import Pyro4 
# using Python3.4.2 

if __name__ == '__main__': 
    uri = 'PYRO:[email protected]:9999' 
    remote = Pyro4.Proxy(uri) 
    response = remote.hello('hello') 
    print('server said {}'.format(response)) 
    remote.shutdown() 
    remote._pyroRelease() 
    print('client exiting')