Como você executa um script Python como um serviço no Windows?

260

Estou desenhando a arquitetura de um conjunto de programas que compartilham vários objetos inter-relacionados armazenados em um banco de dados. Quero que um dos programas atue como um serviço que forneça uma interface de nível superior para operações nesses objetos, e os outros programas acessem os objetos por meio desse serviço.

Atualmente, estou visando o Python e a estrutura do Django como as tecnologias para implementar esse serviço. Tenho certeza de que sei como daemonizar o programa Python no Linux. No entanto, é um item de especificação opcional que o sistema deve oferecer suporte ao Windows. Tenho pouca experiência com a programação do Windows e nenhuma experiência com os serviços do Windows.

É possível executar um programa Python como um serviço do Windows (ou seja, executá-lo automaticamente sem o login do usuário)? Não precisarei necessariamente implementar esta parte, mas preciso de uma idéia aproximada de como isso seria feito para decidir se o projeto deve seguir essas linhas.

Edit: Obrigado por todas as respostas até agora, eles são bastante abrangentes. Gostaria de saber mais uma coisa: como o Windows está ciente do meu serviço? Posso gerenciá-lo com os utilitários nativos do Windows? Qual é o equivalente a colocar um script de início / parada no /etc/init.d?

Hanno Fietz
fonte
2
Verifique este modelo de serviço do Windows que ele usa a API win32service .
CMS

Respostas:

254

Sim você pode. Faço isso usando as bibliotecas pythoncom incluídas no ActivePython ou podem ser instaladas com o pywin32 (extensões do Python para Windows).

Este é um esqueleto básico para um serviço simples:

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)

Seu código seguiria o main()método - geralmente com algum tipo de loop infinito que pode ser interrompido pela verificação de um sinalizador, que você define no SvcStopmétodo

Ricardo Reyes
fonte
21
Depois de codificar isso, como digo ao Windows para executá-lo como um serviço?
Kit
33
@Kit: execute seu script com o da linha de comando com o parâmetro "install". Então você vai ser capaz de ver a sua aplicação na lista de serviços do Windows, onde você pode iniciá-lo, pará-lo, ou defini-lo para iniciar automaticamente
Ricardo Reyes
16
Você menciona especialmente o pythoncom e o importa no seu código de exemplo. O problema é que você nunca usa pythoncom em qualquer lugar do seu código de exemplo, apenas importa. Por que fazer uma menção especial e depois não mostrar seu uso?
precisa saber é o seguinte
10
Por que para o socket.setdefaulttimeout(60)é? É necessário para um serviço ou foi apenas acidentalmente copiado de algum serviço existente? :)
Timur
7
chrisumbel.com/article/windows_services_in_python Este é um exemplo semelhante, mas mais completa
csprabala
41

Embora eu tenha votado positivamente na resposta escolhida há algumas semanas, nesse meio tempo, lutei muito mais com esse tópico. Parece que ter uma instalação especial do Python e usar módulos especiais para executar um script como serviço é simplesmente o caminho errado. E a portabilidade e tal?

Encontrei o maravilhoso Gerenciador de serviços não sugadores , o que tornava realmente simples e sensato lidar com os Serviços do Windows. Imaginei que, como eu poderia passar opções para um serviço instalado, também poderia selecionar meu executável Python e passar meu script como uma opção.

Ainda não tentei esta solução, mas vou fazê-lo agora e atualizar esta postagem ao longo do processo. Também estou interessado em usar virtualenvs no Windows, para que eu possa criar um tutorial mais cedo ou mais tarde e vincular aqui.

mknaf
fonte
Alguma sorte? Estou construindo um site muito simples para um cliente e não preciso usar toda a pilha do Apache. Também a construção do serviço soou como um convite para problemas também, como já li em outros comentários.
Jaran 17/08/14
Sim, isso funciona e é muito fácil de fazer. Você acabou de dar o caminho e os argumentos para o script. Consegui que o meu funcionasse sem um console, caso alguém acabasse com uma janela do console.
precisa saber é o seguinte
Enquanto isso aparentemente funciona, existem outras dificuldades, especialmente quando você "não precisa usar toda a pilha do Apache": o gunicorn, por exemplo, ainda não roda no Windows, o que na verdade foi o principal obstáculo para mim.
mknaf 9/09/14
4
O truque aqui é para executar python.exe como um serviço e seu script python como o parâmetro: como "NSSM instalar MyServiceName c: \ python27 \ python.exe c: \ temp \ myscript.py"
poleguy
Funciona bem! Em um sistema com vários ambientes virtuais, o caminho pode fazer referência ao interpretador Python exe no diretório Scripts do ambiente virtual desejado. Parece que new-serviceno PowerShell deveria ser capaz de fazer isso, mas iniciar (e monitorar) um script como serviço evidentemente envolve muito mais detalhes, dos quais o nssm cuida muito bem.
Fred Schleifer
26

A maneira mais simples é usar o: NSSM - o Non-Sucking Service Manager:

1 - faça o download em https://nssm.cc/download

2 - instale o programa python como um serviço: Win prompt as admin

c:> nssm.exe instala o WinService

3 - No console do NSSM:

caminho: C: \ Python27 \ Python27.exe

Diretório de inicialização: C: \ Python27

Argumentos: c: \ WinService.py

4 - verifique os serviços criados em services.msc

Adriano RPL
fonte
Eu costumava usar o nssm.exe para instalar meu Visual Studio C ++ .exe como serviço e agora posso usar o nssm.exe também para o meu Python .pyc como serviço. Obrigado.
Etoricky 7/05/19
Nota: se o script * .py estiver localizado em uma pasta com espaço (por exemplo: C: \ Arquivos de Programas \ myapp.py), será necessário especificar argumentos entre aspas: Argumentos: "C: \ Arquivos de Programas \ myapp.py"
Yury Kozlov
Como fornecer um ambiente virtual?
shaik moeed
24

A maneira mais simples de conseguir isso é usar o comando nativo sc.exe:

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

Referências:

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. Ao criar um serviço com sc.exe, como passar parâmetros de contexto?
pyOwner
fonte
Eu acho que é um problema com seu próprio comando ou aplicativo. De qualquer forma, verifique este support.microsoft.com/en-us/help/886695/…
pyOwner
Meu aplicativo funciona bem fora do serviço e usei o mesmo código acima sem resultado.
Neroesam
Como fornecer um ambiente virtual?
shaik moeed
Você tentou virtualenv?
PyOwner 5/12/19
1
Isso não funciona. Um serviço do Windows deve expor uma determinada interface que o pacote pywin32 faz. No entanto, um script Python simples e antigo não será suficiente.
Siddhartha Gandhi
23

Existem algumas alternativas para instalar como serviço praticamente qualquer executável do Windows.

Método 1: usar instsrv e srvany do rktools.exe

Para o Windows Home Server ou o Windows Server 2003 (também funciona com o WinXP), o Windows Server 2003 Resource Kit Tools vem com utilitários que podem ser usados ​​em conjunto para isso, chamados instsrv.exe e srvany.exe . Consulte este artigo da KB KB787890 da Microsoft para obter detalhes sobre como usar esses utilitários.

Para o Windows Home Server, existe um ótimo invólucro fácil de usar para esses utilitários chamado apropriadamente " Any Service Installer ".

Método 2: Use ServiceInstaller para Windows NT

Há outra alternativa usando o ServiceInstaller para Windows NT (disponível para download aqui ) com instruções em python disponíveis . Ao contrário do nome, ele funciona tanto com o Windows 2000 quanto com o Windows XP. Aqui estão algumas instruções sobre como instalar um script python como um serviço.

Instalando um script Python

Execute ServiceInstaller para criar um novo serviço. (Neste exemplo, supõe-se que o python esteja instalado em 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

Após a instalação, abra o miniaplicativo Serviços do Painel de Controle, selecione e inicie o serviço PythonTest.

Após minha resposta inicial, notei que havia perguntas e respostas estreitamente relacionadas já publicadas no SO. Veja também:

Posso executar um script Python como um serviço (no Windows)? Quão?

Como conscientizo o Windows sobre um serviço que escrevi em Python?

popcnt
fonte
Eu só notei há outro Q & A semelhante já: stackoverflow.com/questions/32404/... stackoverflow.com/questions/34328/...
POPCNT
O Service Installer não funciona em uma arquitetura de 64 bits, portanto a opção 1 se torna a opção goto.
Noah Campbell
O link acima para ServiceInstaller não funciona mais. Encontrei-o aqui: sites.google.com/site/conort/…
LarsH 14/11
2
Em suma, acho que não NTseria necessariamente "contrário" ao nome, pelo menos não no discurso popular entre programadores. Apenas se refere à " arquitetura NT ", em oposição à " marca NT ". Dito isto, de acordo com a conversa na wikipedia, isso está em debate, uma vez que "não é um termo oficial da Microsoft", mas ainda assim há uma tradição nessa linha de pensamento.
n611x007
15

Explicação passo a passo de como fazê-lo funcionar:

1- Primeiro crie um arquivo python de acordo com o esqueleto básico mencionado acima. E salve-o em um caminho, por exemplo: "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 - Nesta etapa, devemos registrar nosso serviço.

Execute o prompt de comando como administrador e digite como:

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

o primeiro argumento do binpath é o caminho do python.exe

O segundo argumento do binpath é o caminho do seu arquivo python que criamos já

Não perca que você deve colocar um espaço após cada sinal " = ".

Então, se está tudo bem, você deve ver

[SC] SUCESSO CreateService

Agora seu serviço python está instalado como serviço do Windows agora. Você pode vê-lo no Service Manager e no registro em:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ TestService

3- Ok agora. Você pode iniciar seu serviço no gerenciador de serviços.

Você pode executar todos os arquivos python que fornecem esse esqueleto de serviço.

Seckin Sanli
fonte
Existem muitos exemplos ruins de como usar SetEvent(self.hWaitStop)e WaitForSingleObject. Provavelmente, com base na cópia impensada da resposta selecionada aqui. Essa é uma boa maneira de fazer isso que funcione corretamente para os argumentos "debug" e "stop". (A parte sobre o uso do SC parece redundante quando HandleCommandLinefaz o trabalho e pode executar a depuração.)
Alias_Knagg
3

pysc: Service Control Manager em Python

Exemplo de script a ser executado como um serviço obtido em 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()

Criar e iniciar serviço

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

Parar e excluir serviço

import pysc

service_name = 'test_xmlrpc_server'

pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc
Seliverstov Maksim
fonte
3
Alguém sabe por que isso teve um voto negativo? Parece uma boa solução.
Jarrod Chesney
3

Comecei a hospedagem como um serviço com pywin32 .

Tudo estava bem, mas eu encontrei o problema de que o serviço não era capaz de iniciar dentro de 30 segundos (tempo limite padrão para Windows) na inicialização do sistema. Isso foi fundamental para mim, porque a inicialização do Windows ocorreu simultaneamente em várias máquinas virtuais hospedadas em uma máquina física e a carga de IO foi enorme. As mensagens de erro foram:

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.

Eu lutei muito com o pywin, mas acabei usando o NSSM, como foi proposto nesta resposta . Foi muito fácil migrar para ele.

flam3
fonte
2

nssm em python 3+

(Eu converti meu arquivo .py para .exe com pyinstaller )

nssm: como dito antes

  • execute a instalação do nssm {ServiceName}
  • No console do NSSM:

    caminho: caminho \ para \ seu \ programa.exe

    Diretório de inicialização: path \ to \ your \ #same como o caminho, mas sem o seu program.exe

    Argumentos: vazio

Se você não deseja converter seu projeto para .exe

  • crie um arquivo .bat com python {{your python.py file name}}
  • e defina o caminho para o arquivo .bat
codificador
fonte
Como fornecer um ambiente virtual?
shaik moeed
1

Um exemplo completo do pywin32 usando loop ou subthread

Depois de trabalhar nisso por alguns dias, aqui está a resposta que eu gostaria de encontrar, usando o pywin32 para mantê-lo agradável e independente.

Esse é um código de trabalho completo para uma solução baseada em loop e outra baseada em encadeamento. Pode funcionar em python 2 e 3, embora eu só tenha testado a versão mais recente no 2.7 e no Win7. O loop deve ser bom para pesquisar código e a banda de rodagem deve funcionar com mais código semelhante ao servidor. Parece funcionar bem com o servidor wsgi da garçonete que não possui uma maneira padrão de desligar normalmente.

Também gostaria de observar que parece haver muitos exemplos por aí, como este, que são quase úteis, mas na realidade enganosos, porque eles cortaram e colaram outros exemplos cegamente. Eu poderia estar errado. mas por que criar um evento se você nunca o espera?

Dito isto, ainda sinto que estou em um terreno um pouco instável aqui, especialmente no que diz respeito à limpeza da saída da versão do encadeamento, mas pelo menos acredito que não há nada enganoso aqui.

Para executar, copie o código para um arquivo e siga as instruções.

atualizar:

Use um sinalizador simples para finalizar o encadeamento. O ponto importante é que o "thread done" é impresso.
Para um exemplo mais elaborado saindo de um encadeamento de servidor não cooperativo, veja minha postagem sobre o servidor wsgi de garçonete .

# 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
fonte
0

A resposta aceita usando win32serviceutilfunciona, mas é complicada e dificulta a depuração e as alterações. É muito mais fácil usar o NSSM ( o Non-Sucking Service Manager) . Você escreve e depura confortavelmente um programa python normal e, quando ele finalmente funciona, você usa o NSSM para instalá-lo como um serviço em menos de um minuto:

Em um prompt de comando elevado (admin), você executa nssm.exe install NameOfYourServicee preenche estas opções:

  • path : (o caminho para python.exe, por exemplo C:\Python27\Python.exe)
  • Argumentos : (o caminho para o seu script python, por exemplo c:\path\to\program.py)

A propósito, se o seu programa imprimir mensagens úteis que você deseja manter em um arquivo de log, o NSSM também poderá lidar com isso e muito mais para você.

ndemou
fonte
Sim, esta é uma duplicata da resposta de Adriano. Votei positivamente nessa resposta e tentei editá-la, mas após as edições estava procurando uma nova resposta.
Ndemou
Como fornecer ambiente virtual?
shaik moeed
0

Para quem deseja criar serviço em VENV ou Pycharm !!!!!!!

Depois de ler todas as respostas e criar alguns scripts, se você puder executar python service.py installe python service.py debug, mas python service.py startnão tiver resposta.

Talvez seja causado por um problema venv, porque o serviço windows inicia seu serviço pelo exec PROJECT\venv\Lib\site-packages\win32\pythonservice.exe.

Você pode usar powershellou cmdtestar seu serviço para encontrar mais detalhes dos erros.

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 você receber algum erro como eu, poderá verificar minha resposta em outra pergunta, corrigi-a e postei meu código aqui .

oraant
fonte
-1

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

  1. Acompanhe o PySvc.py

  2. alterando a pasta dll

Eu sei que isso é velho, mas eu fiquei preso nisso para sempre. Para mim, esse problema específico foi resolvido copiando este arquivo - pywintypes36.dll

De -> Python36 \ Lib \ pacotes de sites \ pywin32_system32

Para -> Python36 \ Lib \ pacotes de sites \ 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. alterando o caminho para a pasta python

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

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