Come si esegue uno script Python come servizio in Windows?


260

Sto disegnando l'architettura per un insieme di programmi che condividono vari oggetti correlati archiviati in un database. Voglio che uno dei programmi funga da servizio che fornisca un'interfaccia di livello superiore per le operazioni su questi oggetti e che gli altri programmi accedano agli oggetti attraverso quel servizio.

Attualmente sto puntando a Python e al framework Django come tecnologie per implementare quel servizio. Sono abbastanza sicuro di capire come demonizzare il programma Python in Linux. Tuttavia, è un elemento di specifica facoltativo che il sistema dovrebbe supportare Windows. Ho poca esperienza con la programmazione di Windows e nessuna esperienza con i servizi di Windows.

È possibile eseguire un programma Python come servizio di Windows (ovvero eseguirlo automaticamente senza il login dell'utente)? Non dovrò necessariamente implementare questa parte, ma ho bisogno di un'idea approssimativa di come sarebbe fatto per decidere se progettare in questo senso.

Modifica: grazie per tutte le risposte finora, sono abbastanza complete. Vorrei sapere un'altra cosa: in che modo Windows è a conoscenza del mio servizio? Posso gestirlo con le utility native di Windows? Qual è l'equivalente di mettere uno script start / stop in /etc/init.d?


2
Controlla questo modello di servizio di Windows che utilizza l' API win32service .
CMS,

Risposte:


254

Si, puoi. Lo faccio usando le librerie pythoncom fornite con ActivePython o che possono essere installate con pywin32 (estensioni Python per Windows).

Questo è uno scheletro di base per un servizio semplice:

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)

Il tuo codice verrebbe inserito nel main()metodo, di solito con una sorta di ciclo infinito che potrebbe essere interrotto controllando un flag, che hai impostato nel SvcStopmetodo


21
Dopo aver codificato questo, come posso dire a Windows di eseguire questo come servizio?
Kit

33
@Kit: esegui il tuo script con dalla riga di comando con il parametro "installa". Quindi sarai in grado di vedere la tua applicazione nell'elenco dei servizi di Windows, dove puoi avviarla, interromperla o impostarla per l'avvio automatico
Ricardo Reyes

16
Diamo una menzione speciale a pythoncom e lo importate nel vostro codice di esempio. Il problema è che in realtà non usi mai pythoncom da nessuna parte nel tuo codice di esempio, lo importi solo. Perché dare una menzione speciale e quindi non mostrare il suo utilizzo?
Pulsanti 840,

10
Perché per socket.setdefaulttimeout(60)è? È necessario per un servizio o è stato copiato per sbaglio da un servizio esistente? :)
Timur,

7
chrisumbel.com/article/windows_services_in_python Questo è un esempio simile ma più completo
csprabala,

41

Anche se ho votato a favore della risposta scelta un paio di settimane fa, nel frattempo ho faticato molto di più su questo argomento. È come avere un'installazione speciale di Python e usare moduli speciali per eseguire uno script come servizio è semplicemente il modo sbagliato. Che dire della portabilità e simili?

Mi sono imbattuto nel meraviglioso Service Manager non succhiante , che ha reso davvero semplice e sano gestire i servizi di Windows. Ho pensato che potendo passare le opzioni a un servizio installato, avrei potuto anche selezionare il mio eseguibile Python e passare il mio script come opzione.

Non ho ancora provato questa soluzione, ma lo farò ora e aggiornerò questo post lungo il processo. Sono anche interessato all'utilizzo di virtualenvs su Windows, quindi potrei trovare un tutorial prima o poi e collegarmi qui.


Qualche fortuna? Sto costruendo un sito molto semplice per un client e non ho bisogno di usare l'intero stack di Apache. Anche costruire il servizio da solo è sembrato un invito a creare problemi, come ho letto da altri commenti.
Jaran,

Sì, funziona ed è molto facile da fare. Devi solo fornire il percorso e gli argomenti per lo script. Sono stato in grado di far funzionare il mio senza una console nel caso in cui qualcuno finisse con una finestra della console in qualche modo.
kmcguire,

Mentre questo sembra funzionare, ci sono altre difficoltà soprattutto quando "non è necessario utilizzare l'intero stack di Apache": gunicorn, ad esempio, non funziona ancora su Windows, che in realtà è stato lo showtopper per me.
mknaf,

4
Il trucco qui è eseguire python.exe come servizio e lo script python come parametro: come "nssm install MyServiceName c: \ python27 \ python.exe c: \ temp \ myscript.py"
poleguy

Funziona alla grande! Su un sistema con più ambienti virtuali, il percorso può fare riferimento all'esempio dell'interprete Python nella directory Scripts dell'ambiente virtuale desiderato. Sembra che new-servicein PowerShell dovrebbe essere in grado di farlo, ma l'avvio (e il monitoraggio) di uno script come servizio comporta evidentemente molti più dettagli, di cui nssm si occupa molto bene.
Fred Schleifer,

26

Il modo più semplice è usare: NSSM - il gestore del servizio non succhiante:

1 - effettua il download su https://nssm.cc/download

2 - installa il programma python come servizio: Win prompt come admin

c:> nssm.exe installa WinService

3 - Sulla console di NSSM:

percorso: C: \ Python27 \ Python27.exe

Directory di avvio: C: \ Python27

Argomenti: c: \ WinService.py

4 - controlla i servizi creati su services.msc


Ho usato nssm.exe per installare Visual Studio C ++ .exe come servizio e ora posso usare anche nssm.exe per il mio Python .pyc come servizio. Grazie.
etoricky

Nota: se lo script * .py si trova in una cartella con spazio (ad esempio: C: \ Programmi \ myapp.py) è necessario specificare gli argomenti tra virgolette: Argomenti: "C: \ Programmi \ myapp.py"
Yury Kozlov

Come fornire un ambiente virtuale?
Shaik Moeed,

24

Il modo più semplice per raggiungere questo obiettivo è utilizzare il comando nativo sc.exe:

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

Riferimenti:

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. Quando si crea un servizio con sc.exe come passare i parametri di contesto?

Penso che sia un problema con il tuo comando o l'applicazione stessa. Ad ogni modo, controlla questo support.microsoft.com/en-us/help/886695/…
pyOwner

La mia app funziona benissimo al di fuori del servizio e ho usato lo stesso codice sopra senza risultati.
nimeresam,

Come fornire un ambiente virtuale?
Shaik Moeed,

Hai provato virtualenv?
pyOwner,

1
Questo non funziona Un servizio Windows deve esporre una determinata interfaccia che fa il pacchetto pywin32. Tuttavia, un semplice script Python non sarà sufficiente.
Siddhartha Gandhi,

23

Esistono un paio di alternative per l'installazione come servizio praticamente di qualsiasi eseguibile di Windows.

Metodo 1: utilizzare instsrv e srvany da rktools.exe

Per Windows Home Server o Windows Server 2003 (funziona anche con WinXP), gli strumenti del Resource Kit di Windows Server 2003 sono dotati di utility che possono essere utilizzate in questo caso, chiamate instsrv.exe e srvany.exe . Vedi questo articolo di Microsoft KB KB137890 per i dettagli su come utilizzare questi programmi di utilità.

Per Windows Home Server, esiste un ottimo wrapper facile da usare per queste utility chiamato appropriatamente " Any Service Installer ".

Metodo 2: utilizzare ServiceInstaller per Windows NT

Esiste un'altra alternativa che utilizza ServiceInstaller per Windows NT ( scaricabile qui ) con le istruzioni di Python disponibili . Contrariamente al nome, funziona anche con Windows 2000 e Windows XP. Ecco alcune istruzioni su come installare uno script Python come servizio.

Installazione di uno script Python

Eseguire ServiceInstaller per creare un nuovo servizio. (In questo esempio, si presume che python sia installato in 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

Dopo l'installazione, aprire l'applet Servizi del pannello di controllo, selezionare e avviare il servizio PythonTest.

Dopo la mia risposta iniziale, ho notato che c'erano già domande e risposte strettamente correlate pubblicate su SO. Guarda anche:

Posso eseguire uno script Python come servizio (in Windows)? Come?

Come faccio a rendere Windows consapevole di un servizio che ho scritto in Python?



Service Installer non funziona su un'architettura a 64 bit, quindi l'opzione 1 diventa l'opzione goto.
Noah Campbell,

Il link sopra a ServiceInstaller non funziona più. L'ho trovato qui: sites.google.com/site/conort/…
LarsH

2
fuori nota, non penso che NTsarebbe necessariamente "contrario" al nome, almeno non nel discorso popolare programmatore. Si riferisce solo all '" architettura NT ", al contrario del " marchio NT ". Detto questo, secondo i discorsi su Wikipedia questo è oggetto di dibattito, dal momento che "non è un termine ufficiale di Microsoft", ma esiste comunque una tradizione con questa linea di pensiero.
n611x007,

15

Spiegazione dettagliata su come farlo funzionare:

1- Per prima cosa crea un file Python secondo lo scheletro di base sopra menzionato. E salvalo in un percorso, ad esempio: "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 - In questo passaggio dovremmo registrare il nostro servizio.

Esegui il prompt dei comandi come amministratore e digita come:

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

il primo argomento di binpath è il percorso di python.exe

il secondo argomento di binpath è il percorso del tuo file Python che abbiamo già creato

Non perdere che dovresti inserire uno spazio dopo ogni segno " = ".

Quindi se tutto va bene, dovresti vedere

[SC] CreateService SUCCESS

Ora il tuo servizio Python è ora installato come servizio Windows. Puoi vederlo in Service Manager e registro in:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ TestService

3- Ok adesso. È possibile avviare il servizio su Service Manager.

È possibile eseguire ogni file Python che fornisce questo scheletro di servizio.


Ci sono molti cattivi esempi là fuori su come usare SetEvent(self.hWaitStop)e WaitForSingleObject. Basato sulla copia spensierata della risposta selezionata qui probabilmente. Questo è un buon modo per farlo che funziona in modo pulito sia per il "debug" che per gli argomenti "stop". (La parte sull'uso di SC sembra ridondante quando HandleCommandLinefa il lavoro e può eseguire il debug.)
Alias_Knagg

3

pysc: Service Control Manager su Python

Script di esempio da eseguire come servizio tratto da 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()

Crea e avvia il servizio

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'))

Interrompere ed eliminare il servizio

import pysc

service_name = 'test_xmlrpc_server'

pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc

3
Qualcuno sa perché questo ha ottenuto un downvote? Sembra una bella soluzione.
Jarrod Chesney,

3

Ho iniziato l'hosting come servizio con pywin32 .

Tutto è andato bene, ma ho riscontrato il problema che il servizio non è stato in grado di avviarsi entro 30 secondi (timeout predefinito per Windows) all'avvio del sistema. Per me è stato fondamentale perché l'avvio di Windows ha avuto luogo simultaneamente su diverse macchine virtuali ospitate su una macchina fisica e il carico di I / O è stato enorme. I messaggi di errore erano:

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.

Ho combattuto molto con Pywin, ma alla fine ho usato NSSM come è stato proposto in questa risposta . È stato molto facile migrare verso di esso.


2

nssm in python 3+

(Ho convertito il mio file .py in .exe con pyinstaller )

nssm: come detto prima

  • esegui nssm install {ServiceName}
  • Sulla console di NSSM:

    percorso: percorso \ a \ tuo \ programma.exe

    Directory di avvio: percorso \ to \ your \ #same come percorso ma senza program.exe

    Argomenti: vuoto

Se non vuoi convertire il tuo progetto in .exe

  • crea un file .bat con python {{your python.py file name}}
  • e imposta il percorso del file .bat

Come fornire un ambiente virtuale?
Shaik Moeed,

1

Un esempio completo di pywin32 che utilizza loop o subthread

Dopo aver lavorato su questo per alcuni giorni, ecco la risposta che avrei voluto trovare, usando pywin32 per mantenerlo piacevole e autonomo.

Questo è un codice di lavoro completo per una soluzione basata su loop e una basata su thread. Può funzionare sia su Python 2 che su 3, anche se ho testato solo l'ultima versione su 2.7 e Win7. Il ciclo dovrebbe essere buono per il codice di polling e il battistrada dovrebbe funzionare con più codice simile al server. Sembra funzionare bene con il server wsgi cameriera che non ha un modo standard per chiudere con grazia.

Vorrei anche notare che sembrano esserci molti esempi là fuori, come questo che sono quasi utili, ma in realtà fuorvianti, perché hanno tagliato e incollato ciecamente altri esempi. Potrei sbagliarmi. ma perché creare un evento se non lo aspetti mai?

Detto questo, mi sento ancora su un terreno un po 'traballante qui, soprattutto per quanto riguarda l'uscita pulita dalla versione del thread, ma almeno credo che qui non ci sia nulla di fuorviante .

Per eseguire è sufficiente copiare il codice in un file e seguire le istruzioni.

aggiornare:

Usa un semplice flag per terminare il thread. La parte importante è che "thread thread" viene stampato.
Per un esempio più elaborato che esce da un thread del server non collaborativo, vedere il mio post sul server wsgi della cameriera .

# 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()

0

La risposta accettata usando win32serviceutilfunziona ma è complicata e rende più difficile il debug e le modifiche. È molto più semplice utilizzare NSSM ( il gestore del servizio non succhiante) . Scrivi e debug comodamente un normale programma Python e quando finalmente funziona usi NSSM per installarlo come servizio in meno di un minuto:

Da un prompt dei comandi con privilegi elevati (admin) che esegui nssm.exe install NameOfYourServicee compili queste opzioni:

  • percorso : (il percorso di python.exe ad esempio C:\Python27\Python.exe)
  • Argomenti : (il percorso del tuo script Python, ad esempio c:\path\to\program.py)

A proposito, se il tuo programma stampa messaggi utili che vuoi conservare in un file di registro, NSSM può anche gestire questo e molto altro per te.


Sì, questo è un duplicato della risposta di Adriano. Ho votato a favore di quella risposta e ho provato a modificarla, ma dopo le modifiche stavo guardando una nuova risposta.
ndemou,

Come fornire ambiente virtuale?
Shaik Moeed,

0

Per chiunque voglia creare un servizio in VENV o Pycharm !!!!!!!

Dopo aver letto tutte le risposte e creare alcuni script, se è possibile eseguire python service.py installe python service.py debug, mapython service.py start non ha alcuna risposta.

Forse è causato da un problema venv, perché il servizio di Windows avvia il tuo servizio da exec PROJECT\venv\Lib\site-packages\win32\pythonservice.exe.

È possibile utilizzare powershello cmdper testare il servizio per trovare ulteriori dettagli sull'errore.

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)

Se ricevi un errore come me, puoi controllare la mia risposta in un'altra domanda, l'ho risolto e postare il mio codice qui .


-1

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

  1. Segui il PySvc.py

  2. cambiando la cartella dll

So che questo è vecchio ma ero bloccato su questo per sempre. Per me, questo problema specifico è stato risolto copiando questo file - pywintypes36.dll

Da -> Python36 \ Lib \ site-pacchetti \ pywin32_system32

A -> Python36 \ Lib \ site-pacchetti \ 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 il percorso nella cartella python di

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

  1. NET START PySvc
  2. NET STOP PySvc
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.