¿Cómo se ejecuta un script Python como servicio en Windows?

260

Estoy esbozando la arquitectura para un conjunto de programas que comparten varios objetos interrelacionados almacenados en una base de datos. Quiero que uno de los programas actúe como un servicio que proporciona una interfaz de nivel superior para las operaciones en estos objetos, y que los otros programas accedan a los objetos a través de ese servicio.

Actualmente estoy apuntando a Python y al framework Django como las tecnologías para implementar ese servicio. Estoy bastante seguro de que imagino cómo demonizar el programa Python en Linux. Sin embargo, es un elemento de especificación opcional que el sistema debería admitir Windows. Tengo poca experiencia con la programación de Windows y ninguna experiencia con los servicios de Windows.

¿Es posible ejecutar un programa Python como un servicio de Windows (es decir, ejecutarlo automáticamente sin inicio de sesión del usuario)? No necesariamente tendré que implementar esta parte, pero necesito una idea aproximada de cómo se haría para decidir si se diseña en esta línea.

Editar: Gracias por todas las respuestas hasta ahora, son bastante completas. Quisiera saber una cosa más: ¿cómo es Windows consciente de mi servicio? ¿Puedo administrarlo con las utilidades nativas de Windows? ¿Cuál es el equivalente de poner un script de inicio / detención en /etc/init.d?

Hanno Fietz
fuente
2
Consulte esta plantilla de servicio de Windows que utiliza la API win32service .
CMS

Respuestas:

254

Sí tu puedes. Lo hago usando las bibliotecas de pythoncom que vienen incluidas con ActivePython o que se pueden instalar con pywin32 ( extensiones de Python para Windows).

Este es un esqueleto básico para un servicio simple:

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"

    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    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.main()

    def main(self):
        pass

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

Su código iría en el main()método, generalmente con algún tipo de bucle infinito que podría interrumpirse al marcar una bandera, que configuró en el SvcStopmétodo

Ricardo Reyes
fuente
21
Después de codificar esto, ¿cómo le digo a Windows que ejecute esto como un servicio?
Kit
33
@ Kit: ejecute su script con la línea de comando con el parámetro "instalar". A continuación, podrás ver su aplicación en la lista de servicios de Windows', donde se puede iniciar, detener, o configurarlo para iniciar automáticamente
Ricardo Reyes
16
Le das una mención especial a pythoncom, y lo importas en tu código de ejemplo. El problema es que nunca usas pythoncom en ninguna parte de tu código de ejemplo, solo lo importas. ¿Por qué darle una mención especial y luego no mostrar su uso?
Botones840
10
¿Por qué para el socket.setdefaulttimeout(60)es? ¿Es necesario para un servicio o simplemente se copió accidentalmente de algún servicio existente? :)
Timur
77
chrisumbel.com/article/windows_services_in_python Este es un ejemplo similar pero más completo
csprabala
41

Aunque voté por la respuesta elegida hace un par de semanas, mientras tanto, luché mucho más con este tema. Parece que tener una instalación especial de Python y usar módulos especiales para ejecutar un script como servicio es simplemente la forma incorrecta. ¿Qué pasa con la portabilidad y tal?

Me topé con el maravilloso administrador de servicios sin succión , lo que hizo que fuera realmente simple y sensato tratar con los servicios de Windows. Pensé que, dado que podía pasar opciones a un servicio instalado, podría seleccionar mi ejecutable de Python y pasar mi script como una opción.

Todavía no he probado esta solución, pero lo haré ahora y actualizaré esta publicación a lo largo del proceso. También estoy interesado en usar virtualenvs en Windows, por lo que podría idear un tutorial tarde o temprano y vincularlo aquí.

mknaf
fuente
¿Alguna suerte? Estoy construyendo un sitio muy simple para un cliente y no necesito usar toda la pila de Apache. También construir el servicio por mí mismo también ha parecido una invitación a los problemas, como he leído en otros comentarios.
Jaran
Sí, esto funciona y es muy fácil de hacer. Simplemente da la ruta y los argumentos para el guión. Pude hacer que el mío se ejecute sin una consola en caso de que alguien termine de alguna manera con una ventana de consola.
kmcguire
Si bien esto aparentemente funciona, existen otras dificultades, especialmente cuando "no necesita usar toda la pila de Apache": gunicorn, por ejemplo, todavía no se ejecuta en Windows, lo que en realidad fue lo mejor para mí.
mknaf
44
El truco aquí es ejecutar python.exe como un servicio y su script de python como parámetro: como "nssm install MyServiceName c: \ python27 \ python.exe c: \ temp \ myscript.py"
poleguy
¡Funciona genial! En un sistema con múltiples entornos virtuales, la ruta puede hacer referencia al intérprete de Python exe en el directorio Scripts del entorno virtual deseado. Parece que new-serviceen PowerShell debería poder hacer esto, pero iniciar (y monitorear) un script como servicio evidentemente implica muchos más detalles, lo que nssm se ocupa muy bien.
Fred Schleifer
26

La forma más sencilla es utilizar: NSSM, el Administrador de servicios sin succión:

1 - realice la descarga en https://nssm.cc/download

2 - instala el programa python como un servicio: Win prompt como administrador

c:> nssm.exe instalar WinService

3 - En la consola de NSSM:

ruta: C: \ Python27 \ Python27.exe

Directorio de inicio: C: \ Python27

Argumentos: c: \ WinService.py

4 - verifique los servicios creados en services.msc

Adriano RPL
fuente
Solía ​​usar nssm.exe para instalar mi Visual Studio C ++ .exe como servicio, y ahora también puedo usar nssm.exe para mi Python .pyc como servicio. Gracias.
etoricky
Nota: si su script * .py se encuentra en una carpeta con espacio (por ejemplo: C: \ Archivos de programa \ myapp.py) necesita especificar argumentos entre comillas: Argumentos: "C: \ Archivos de programa \ myapp.py"
Yury Kozlov
¿Cómo proporcionar un entorno virtual?
Shaik Moeed
24

La forma más sencilla de lograr esto es usar el comando nativo sc.exe:

sc create PythonApp binPath= "C:\Python34\Python.exe --C:\tmp\pythonscript.py"

Referencias

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. Al crear un servicio con sc.exe, ¿cómo pasar los parámetros en contexto?
pyOwner
fuente
Creo que es un problema con su comando o aplicación en sí. De todos modos, consulte este support.microsoft.com/en-us/help/886695/…
pyOwner
Mi aplicación funciona bien fuera del servicio, y usé el mismo código anterior sin resultado.
nimeresam
¿Cómo proporcionar un entorno virtual?
Shaik Moeed
¿Probaste virtualenv?
pyOwner
1
Esto no funciona Un servicio de Windows debe exponer una determinada interfaz que hace el paquete pywin32. Sin embargo, un script Python antiguo no será suficiente.
Siddhartha Gandhi
23

Hay un par de alternativas para instalar como servicio prácticamente cualquier ejecutable de Windows.

Método 1: Use instsrv y srvany de rktools.exe

Para Windows Home Server o Windows Server 2003 (también funciona con WinXP), las herramientas del Kit de recursos de Windows Server 2003 vienen con utilidades que se pueden usar en conjunto para esto, llamadas instsrv.exe y srvany.exe . Consulte este artículo KB137890 de Microsoft KB para obtener detalles sobre cómo usar estas utilidades.

Para Windows Home Server, hay un excelente contenedor fácil de usar para estas utilidades llamado acertadamente " Any Service Installer ".

Método 2: utilizar ServiceInstaller para Windows NT

Hay otra alternativa que utiliza ServiceInstaller para Windows NT (se puede descargar aquí ) con las instrucciones de Python disponibles . Contrariamente al nombre, también funciona con Windows 2000 y Windows XP. Aquí hay algunas instrucciones sobre cómo instalar un script de Python como servicio.

Instalar un script de Python

Ejecute ServiceInstaller para crear un nuevo servicio. (En este ejemplo, se supone que python está instalado en c: \ python25)

Service Name  : PythonTest
Display Name : PythonTest 
Startup : Manual (or whatever you like)
Dependencies : (Leave blank or fill to fit your needs)
Executable : c:\python25\python.exe
Arguments : c:\path_to_your_python_script\test.py
Working Directory : c:\path_to_your_python_script

Después de la instalación, abra el applet de Servicios del Panel de control, seleccione e inicie el servicio PythonTest.

Después de mi respuesta inicial, noté que ya había preguntas y respuestas estrechamente relacionadas en SO. Ver también:

¿Puedo ejecutar un script Python como servicio (en Windows)? ¿Cómo?

¿Cómo hago para que Windows conozca un servicio que he escrito en Python?

popcnt
fuente
Acabo de notar que ya hay otras preguntas y respuestas similares: stackoverflow.com/questions/32404/… stackoverflow.com/questions/34328/…
popcnt
Service Installer no funciona en una arquitectura de 64 bits, por lo que la opción 1 se convierte en la opción de ir a.
Noah Campbell
El enlace anterior a ServiceInstaller ya no funciona. Lo encontré aquí: sites.google.com/site/conort/…
LarsH
2
Fuera de nota, no creo NTque sea necesariamente "contrario" al nombre, al menos no en el discurso popular del programador. Simplemente se refiere a la " arquitectura NT ", en oposición a la " marca NT ". Dicho esto, de acuerdo con la charla en Wikipedia, esto está a debate, ya que "no es un término oficial de Microsoft", pero sin embargo, existe una tradición con esta línea de pensamiento.
n611x007
15

Explicación paso a paso cómo hacerlo funcionar:

1- Primero crea un archivo python de acuerdo con el esqueleto básico mencionado anteriormente. Y guárdelo en una ruta, por ejemplo: "c: \ PythonFiles \ AppServerSvc.py"

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"


    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    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.main()

    def main(self):
        # Your business logic or call to any class should be here
        # this time it creates a text.txt and writes Test Service in a daily manner 
        f = open('C:\\test.txt', 'a')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            f.write('Test Service  \n')
            f.flush()
            # block for 24*60*60 seconds and wait for a stop event
            # it is used for a one-day loop
            rc = win32event.WaitForSingleObject(self.hWaitStop, 24 * 60 * 60 * 1000)
        f.write('shut down \n')
        f.close()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

2 - En este paso debemos registrar nuestro servicio.

Ejecute el símbolo del sistema como administrador y escriba como:

sc create TestService binpath = "C: \ Python36 \ Python.exe c: \ PythonFiles \ AppServerSvc.py" DisplayName = "TestService" start = auto

El primer argumento de binpath es la ruta de python.exe

El segundo argumento de binpath es la ruta de su archivo Python que ya creamos

No se pierda que debe poner un espacio después de cada signo " = ".

Entonces, si todo está bien, deberías ver

[SC] ÉXITO CreateService

Ahora su servicio de Python está instalado como servicio de Windows ahora. Puede verlo en Service Manager y registro en:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ TestService

3- Ok ahora. Puede iniciar su servicio en el administrador de servicios.

Puede ejecutar cada archivo de Python que proporciona este esqueleto de servicio.

Seckin Sanli
fuente
Hay muchos malos ejemplos de cómo usar SetEvent(self.hWaitStop)y WaitForSingleObject. Basado en una copia irreflexiva de la respuesta seleccionada aquí probablemente. Esta es una buena manera de hacerlo que funciona de manera limpia tanto para la "depuración" como para los argumentos de "detención". (La parte sobre el uso de SC parece redundante cuando HandleCommandLinehace el trabajo, y puede ejecutar la depuración.)
Alias_Knagg
3

pysc: Administrador de control de servicios en Python

Script de ejemplo para ejecutar como un servicio tomado de pythonhosted.org :

from xmlrpc.server import SimpleXMLRPCServer

from pysc import event_stop


class TestServer:

    def echo(self, msg):
        return msg


if __name__ == '__main__':
    server = SimpleXMLRPCServer(('127.0.0.1', 9001))

    @event_stop
    def stop():
        server.server_close()

    server.register_instance(TestServer())
    server.serve_forever()

Crear e iniciar servicio

import os
import sys
from xmlrpc.client import ServerProxy

import pysc


if __name__ == '__main__':
    service_name = 'test_xmlrpc_server'
    script_path = os.path.join(
        os.path.dirname(__file__), 'xmlrpc_server.py'
    )
    pysc.create(
        service_name=service_name,
        cmd=[sys.executable, script_path]
    )
    pysc.start(service_name)

    client = ServerProxy('http://127.0.0.1:9001')
    print(client.echo('test scm'))

Detener y eliminar servicio

import pysc

service_name = 'test_xmlrpc_server'

pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc
Seliverstov Maksim
fuente
3
¿Alguien sabe por qué esto recibió un voto negativo? Parece una buena solución.
Jarrod Chesney
3

Comencé a alojar como servicio con pywin32 .

Todo estaba bien, pero encontré el problema de que el servicio no pudo iniciarse en 30 segundos (tiempo de espera predeterminado para Windows) al iniciar el sistema. Fue crítico para mí porque el inicio de Windows se realizó simultáneamente en varias máquinas virtuales alojadas en una máquina física, y la carga de E / S fue enorme. Los mensajes de error fueron:

Error 1053: The service did not respond to the start or control request in a timely fashion.

Error 7009: Timeout (30000 milliseconds) waiting for the <ServiceName> service to connect.

Luché mucho con pywin, pero terminé usando NSSM como se propuso en esta respuesta . Fue muy fácil migrar a él.

flam3
fuente
2

nssm en python 3+

( Convertí mi archivo .py a .exe con pyinstaller )

nssm: como se dijo antes

  • ejecute nssm install {ServiceName}
  • En la consola de NSSM:

    ruta: ruta \ a \ su \ program.exe

    Directorio de inicio: ruta \ a \ su \ #sama como la ruta pero sin su program.exe

    Argumentos: vacío

Si no desea convertir su proyecto a .exe

  • crear un archivo .bat con python {{your python.py file name}}
  • y establezca la ruta al archivo .bat
descifrador
fuente
¿Cómo proporcionar un entorno virtual?
Shaik Moeed
1

Un ejemplo completo de pywin32 usando bucle o subproceso

Después de trabajar en esto por unos días, aquí está la respuesta que hubiera deseado encontrar, usando pywin32 para mantenerlo agradable y autónomo.

Este es un código de trabajo completo para una solución basada en bucles y una basada en subprocesos. Puede funcionar tanto en python 2 como en 3, aunque solo he probado la última versión en 2.7 y Win7. El bucle debería ser bueno para el código de sondeo, y la banda de rodadura debería funcionar con más código similar al servidor. Parece funcionar bien con el servidor de camarera wsgi que no tiene una forma estándar de cerrar con gracia.

También me gustaría señalar que parece haber muchos ejemplos, como este, que son casi útiles, pero en realidad engañosos, porque han cortado y pegado otros ejemplos a ciegas. Podría estar equivocado. pero ¿por qué crear un evento si nunca lo esperas?

Dicho esto, todavía siento que estoy en un terreno algo inestable aquí, especialmente con respecto a cuán limpia es la salida de la versión de hilo, pero al menos creo que no hay nada engañoso aquí.

Para ejecutar, simplemente copie el código a un archivo y siga las instrucciones.

actualizar:

Use una bandera simple para terminar el hilo. Lo importante es que se imprime "hilo hecho".
Para un ejemplo más elaborado que sale de un hilo de servidor no cooperativo, vea mi publicación sobre el servidor de camarera wsgi .

# uncomment mainthread() or mainloop() call below
# run without parameters to see HandleCommandLine options
# install service with "install" and remove with "remove"
# run with "debug" to see print statements
# with "start" and "stop" watch for files to appear
# check Windows EventViever for log messages

import socket
import sys
import threading
import time
from random import randint
from os import path

import servicemanager
import win32event
import win32service
import win32serviceutil
# see http://timgolden.me.uk/pywin32-docs/contents.html for details


def dummytask_once(msg='once'):
    fn = path.join(path.dirname(__file__),
                '%s_%s.txt' % (msg, randint(1, 10000)))
    with open(fn, 'w') as fh:
        print(fn)
        fh.write('')


def dummytask_loop():
    global do_run
    while do_run:
        dummytask_once(msg='loop')
        time.sleep(3)


class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global do_run
        do_run = True
        print('thread start\n')
        dummytask_loop()
        print('thread done\n')

    def exit(self):
        global do_run
        do_run = False


class SMWinservice(win32serviceutil.ServiceFramework):
    _svc_name_ = 'PyWinSvc'
    _svc_display_name_ = 'Python Windows Service'
    _svc_description_ = 'An example of a windows service in Python'

    @classmethod
    def parse_command_line(cls):
        win32serviceutil.HandleCommandLine(cls)

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.stopEvt = win32event.CreateEvent(None, 0, 0, None)  # create generic event
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STOPPED,
                            (self._svc_name_, ''))
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stopEvt)  # raise event

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STARTED,
                            (self._svc_name_, ''))
        # UNCOMMENT ONE OF THESE
        # self.mainthread()
        # self.mainloop()

    # Wait for stopEvt indefinitely after starting thread.
    def mainthread(self):
        print('main start')
        self.server = MyThread()
        self.server.start()
        print('wait for win32event')
        win32event.WaitForSingleObject(self.stopEvt, win32event.INFINITE)
        self.server.exit()
        print('wait for thread')
        self.server.join()
        print('main done')

    # Wait for stopEvt event in loop.
    def mainloop(self):
        print('loop start')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            dummytask_once()
            rc = win32event.WaitForSingleObject(self.stopEvt, 3000)
        print('loop done')


if __name__ == '__main__':
    SMWinservice.parse_command_line()
Alias_Knagg
fuente
0

La respuesta aceptada usando win32serviceutilfunciona pero es complicada y dificulta la depuración y los cambios. Es mucho más fácil usar NSSM ( el Administrador de servicios sin succión) . Usted escribe y depura cómodamente un programa Python normal y cuando finalmente funciona, usa NSSM para instalarlo como servicio en menos de un minuto:

Desde un símbolo del sistema elevado (administrador), ejecuta nssm.exe install NameOfYourServicey completa estas opciones:

  • ruta : (la ruta a python.exe, por ejemplo C:\Python27\Python.exe)
  • Argumentos : (la ruta a su script de Python, por ejemplo c:\path\to\program.py)

Por cierto, si su programa imprime mensajes útiles que desea mantener en un archivo de registro, NSSM también puede manejar esto y mucho más por usted.

ndemou
fuente
Sí, este es un duplicado de la respuesta de Adriano. Voté esa respuesta e intenté editarla, pero después de las modificaciones estaba buscando una nueva respuesta.
ndemou
¿Cómo proporcionar un entorno virtual?
Shaik Moeed
0

Para cualquiera que quiera crear servicio en VENV o Pycharm !!!!!!!

Después de leer todas las respuestas y crear algunos scripts, si puede ejecutar python service.py instally python service.py debug, pero python service.py startno tiene respuesta.

Tal vez sea causado por un problema venv, porque el servicio de Windows inicia su servicio por exec PROJECT\venv\Lib\site-packages\win32\pythonservice.exe.

Puede usar powershello cmdprobar su servicio para encontrar más detalles del error.

PS C:\Users\oraant> E:

PS E:\> cd \Software\PythonService\venv\Lib\site-packages\win32

PS E:\Software\PythonService\venv\Lib\site-packages\win32> .\pythonservice.exe -debug ttttt
Debugging service ttttt - press Ctrl+C to stop.
Error 0xC0000004 - Python could not import the service's module

Traceback (most recent call last):
  File "E:\Software\PythonService\my_service.py", line 2, in <module>
    import win32serviceutil
ModuleNotFoundError: No module named 'win32serviceutil'

(null): (null)

Si recibe algún error como yo, puede verificar mi respuesta en otra pregunta, lo arreglé y publiqué mi código aquí .

oraant
fuente
-1

https://www.chrisumbel.com/article/windows_services_in_python

  1. Siga el PySvc.py

  2. cambiando la carpeta dll

Sé que esto es viejo pero estaba atrapado en esto para siempre. Para mí, este problema específico se resolvió copiando este archivo - pywintypes36.dll

Desde -> Python36 \ Lib \ site-packages \ pywin32_system32

Para -> Python36 \ Lib \ site-packages \ win32

setx /M PATH "%PATH%;C:\Users\user\AppData\Local\Programs\Python\Python38-32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Scripts;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\pywin32_system32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\win32
  1. cambiando la ruta a la carpeta python por

cd C:\Users\user\AppData\Local\Programs\Python\Python38-32

  1. NET START PySvc
  2. NET STOP PySvc
gunarajulu renganathan
fuente