python - libreria - sockets node
¿Cómo hacer un servicio de Windows python de un servidor matraz/gevent.socketio? (3)
Tengo un macket / gevent SocketIOServer y necesito que funcione como un servicio:
class TeleportService(win32serviceutil.ServiceFramework):
_svc_name_ = "TeleportServer"
_svc_display_name_ = "Teleport Database Backup Service"
_svc_description_ = "More info at www.elmalabarista.com/teleport"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, ''''))
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
runServer()
@werkzeug.serving.run_with_reloader
def runServer():
print ''Listening on %s...'' % WEB_PORT
ws = SocketIOServer((''0.0.0.0'', WEB_PORT),
SharedDataMiddleware(app, {}),
resource="socket.io",
policy_server=False)
gevent.spawn(runTaskManager).link_exception(lambda *args: sys.exit("important_greenlet died"))
ws.serve_forever()
Sin embargo, no puedo entender cómo detenerlo desde SvcStop, y ejecutarlo tiene el extraño comportamiento que el análisis del servicio de los parámetros de la línea de comando ocurre DESPUÉS de que se mata el servidor de ejecución. Esto significa que el servidor del matraz se ejecuta, puedo acceder desde el navegador web, pero el administrador del servicio lo listó como "No iniciado". Por ejemplo, ejecutando en la línea de comando:
C:/Proyectos/TeleportServer>python service.py uninstall <--BAD PARAM, TO MAKE IT OBVIOUS
2013-02-13 16:19:30,786 - DEBUG: Connecting to localhost:9097
* Restarting with reloader
2013-02-13 16:19:32,650 - DEBUG: Connecting to localhost:9097
Listening on 5000...
Growl not available: Teleport Backup Server is started
KeyboardInterrupt <--- HERE I INTERRUPT WITH CTRL-C
Unknown command - ''uninstall''
Usage: ''service.py [options] install|update|remove|start [...]|stop|restart [...
]|debug [...]''
Options for ''install'' and ''update'' commands only:
--username domain/username : The Username the service is to run under
--password password : The password for the username
--startup [manual|auto|disabled] : How the service starts, default = manual
--interactive : Allow the service to interact with the desktop.
--perfmonini file: .ini file to use for registering performance monitor data
Con la sugerencia de eliminar el recargador en vivo, este es el código que queda. Aún así, el mismo problema
def SvcDoRun (self): servicemanager.LogMsg (servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, ''''))
#self.timeout = 640000 #640 seconds / 10 minutes (value is in milliseconds)
self.timeout = 6000 #120 seconds / 2 minutes
# This is how long the service will wait to run / refresh itself (see script below)
notify.debug("Starting service")
ws = getServer()
while 1:
# Wait for service stop signal, if I timeout, loop again
gevent.sleep(0)
rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
# Check to see if self.hWaitStop happened
if rc == win32event.WAIT_OBJECT_0:
# Stop signal encountered
notify.debug("Stopping service")
ws.kill()
servicemanager.LogInfoMsg("TeleportService - STOPPED!") #For Event Log
break
else:
notify.debug("Starting web server")
ws.serve_forever()
El método serve_forever
proviene de BaseServer.serve_forever
. Para detenerlo, debe llamar a BaseServer.shutdown()
o una derivada de este.
En resumen, debe declarar ws
en el ámbito global. Poner este código antes de la definición de clase de Service
es una forma de hacerlo.
ws = None
A continuación, cambie su implementación de Service.SvcStop
a esto:
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
#Tell the serve_forever() loop to stop and wait until it does.
ws.shutdown()
Como ws.shutdown()
ya espera a que el oyente se detenga, puede deshacerse de self.hWaitStop
, a menos que lo use en otro lugar de su código.
Requiere Python 2.6+
No puedo acceder a WSGIRequestHandler
en Flask fuera de la request
, entonces uso Process
.
import win32serviceutil
import win32service
import win32event
import servicemanager
from multiprocessing import Process
from app import app
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = "TestService"
_svc_display_name_ = "Test Service"
_svc_description_ = "Tests Python service framework by receiving and echoing messages over a named pipe"
def __init__(self, *args):
super().__init__(*args)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.process.terminate()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
self.process = Process(target=self.main)
self.process.start()
self.process.run()
def main(self):
app.run()
if __name__ == ''__main__'':
win32serviceutil.HandleCommandLine(Service)
Para detenerlo desde SvcStop, debe almacenar una referencia a "ws" en una variable global (es decir, en algún lugar donde pueda recuperarse más adelante). AFAIK "ws.kill ()" debería finalizar el ciclo.
El decorador run_with_reloader parece ejecutar la función decorada inmediatamente, lo que explicaría por qué se procesa la línea de comandos después de ejecutar el servidor web. ¿Necesita recargar automáticamente, al parecer el decorador solo es necesario cuando necesita recargarse?
ACTUALIZACIÓN: código de servicio de ejemplo agregado
En un proyecto que no usa matraz o gevent utilizo algo como esto (con muchos detalles eliminados):
class Service (win32serviceutil.ServiceFramework):
def __init__(self, *args, **kwds):
self._mainloop = None
win32serviceutil.ServiceFramework.__init__(self, *args, **kwds)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
if self._mainloop is not None:
self._mainloop.shutdown()
def SvcStart(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
self._mainloop = ... .MainLoop()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
try:
self._mainloop.run_forever()
finally:
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
win32serviceutil.HandleCommandLine(Service)