如何在Windows中将Python脚本作为服务运行?


260

我正在草绘一组程序的体系结构,这些程序共享存储在数据库中的各种相互关联的对象。我希望其中一个程序充当服务,为这些对象的操作提供更高级别的接口,而其他程序则通过该服务访问对象。

我目前的目标是将Python和Django框架作为实现该服务的技术。我很确定我知道如何守护Linux中的Python程序。但是,这是系统应支持Windows的可选规格。我几乎没有Windows编程经验,也没有Windows服务经验。

是否可以将Python程序作为Windows服务运行(即在没有用户登录的情况下自动运行它)?我不必一定要实现这一部分,但是我需要一个大概的想法,即如何决定是否按照这些原则进行设计。

编辑:感谢到目前为止的所有答案,它们是相当全面的。我想知道一件事:Windows如何了解我的服务?我可以使用本地Windows实用程序对其进行管理吗? 将启动/停止脚本放在/etc/init.d中相当于什么?


Answers:


254

是的你可以。我使用ActivePython随附的pythoncom库或可以与pywin32(适用于Windows的Python扩展)一起安装的方式来执行此操作。

这是简单服务的基本框架:

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)

您的代码将进入main()方法中-通常带有某种无限循环,可能会通过检查在SvcStop方法中设置的标志而中断


21
编码后,如何告诉Windows将其作为服务运行?
工具包

33
@Kit:在命令行中使用参数“ install”运行脚本。然后,您将可以在Windows的“服务”列表中看到您的应用程序,可以在其中启动,停止它或将其设置为自动启动
Ricardo Reyes

16
您特别提到了pythoncom,并将其导入到示例代码中。问题是您永远不会在示例代码中的任何地方实际使用pythoncom,而只会导入它。为什么要特别提及它,然后不显示其用法呢?
Buttons840 2011年

10
为什么socket.setdefaulttimeout(60)是?是服务需要的,还是只是偶然从某些现有服务复制而来的?:)
Timur

7
chrisumbel.com/article/windows_services_in_python这是一个相似的示例,但更完整
csprabala 2015年

41

尽管几周前我对选定的答案进行了投票,但与此同时,我在该主题上的努力也更大。感觉就像安装了特殊的Python并使用特殊的模块将脚本作为服务运行是错误的方式。那么可移植性又如何呢?

我偶然发现了很棒的Non-sucking Service Manager,它使处理Windows Services变得非常简单和理智。我认为,因为我可以将选项传递给已安装的服务,所以我也可以选择我的Python可执行文件并将脚本作为选项传递。

我尚未尝试此解决方案,但我现在会尝试并在此过程中更新此帖子。我也对在Windows上使用virtualenvs感兴趣,所以我迟早会想出一个教程并链接到这里。


运气好的话?我正在为客户端构建一个非常简单的站点,不需要使用整个Apache堆栈。正如我从其他评论中看到的那样,我自己也建立服务听起来也很麻烦。
Jaran 2014年

是的,这很有效,而且很容易做到。您只需提供脚本的路径和参数。我可以让我在没有控制台的情况下运行,以防万一有人以某种方式结束控制台窗口。
kmcguire 2014年

尽管这显然可行,但是还有其他困难,特别是当您“不需要使用整个Apache堆栈”时:例如gunicorn尚未在Windows上运行,这实际上是我的首选。
mknaf 2014年

4
这里的技巧是将python.exe作为服务运行,并将python脚本作为参数运行:像“ nssm install MyServiceName c:\ python27 \ python.exe c:\ temp \ myscript.py”
poleguy 2015年

很棒!在具有多个虚拟环境的系统上,该路径可以引用所需虚拟环境的Scripts目录中的Python解释器exe。看起来new-service在PowerShell中应该可以做到这一点,但是启动(和监视)脚本即服务显然涉及更多细节,而nssm可以很好地解决这一问题。
Fred Schleifer

26

最简单的方法是使用:NSSM-非吸吮服务管理器:

1-在https://nssm.cc/download上进行下载

2-将python程序作为服务安装:以管理员身份赢得提示

c:> nssm.exe安装WinService

3-在NSSM的控制台上:

路径:C:\ Python27 \ Python27.exe

启动目录:C:\ Python27

参数:c:\ WinService.py

4-检查在services.msc上创建的服务


我曾经使用nssm.exe将Visual Studio C ++ .exe作为服务安装,现在我也可以将nssm.exe用于我的Python .pyc服务。谢谢。
etoricky

注意:如果* .py脚本位于带空格的文件夹中(例如:C:\ Program Files \ myapp.py),则需要在引号中指定参数:参数:“ C:\ Program Files \ myapp.py”
Yury科兹洛夫

如何提供虚拟环境?
shaik moeed '19

24

实现此目的的最简单方法是使用本机命令sc.exe:

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

参考文献:

  1. https://technet.microsoft.com/zh-CN/library/cc990289(v=ws.11).aspx
  2. 使用sc.exe创建服务时如何传递上下文参数?

我认为,这是您的命令或应用程序本身的问题。无论如何,请检查此support.microsoft.com/en-us/help/886695/…–
pyOwner

我的应用程序在服务之外运行良好,并且我使用了与上面相同的代码,但没有结果。
nimeresam,

如何提供虚拟环境?
shaik moeed '19

您尝试过virtualenv吗?
pyOwner

1
这行不通。Windows服务必须公开pywin32软件包所做的特定接口。但是,普通的Python脚本将无法满足要求。
Siddhartha Gandhi

23

实际上,可以将两种Windows可执行文件作为服务安装为几种选择。

方法1:从rktools.exe使用instsrv和srvany

对于Windows Home Server或Windows Server 2003(也可与WinXP一起使用),Windows Server 2003 Resource Kit Tools附带了可以串联使用的实用程序,称为instsrv.exesrvany.exe。有关如何使用这些实用程序的详细信息,请参见此 Microsoft KB文章KB137890

对于Windows Home Server,这些实用程序有一个很好的用户友好包装,其名称为“ Any Service Installer ”。

方法2:使用Windows NT的ServiceInstaller

还有另一种使用ServiceInstaller for Windows NT的替代方法(可在此处下载),其中提供了python说明。与名称相反,它也适用于Windows 2000和Windows XP。以下是有关如何将python脚本作为服务安装的一些说明。

安装Python脚本

运行ServiceInstaller以创建新服务。(在此示例中,假定python安装在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

安装后,打开“控制面板”的“服务”小程序,选择并启动PythonTest服务。

在最初回答之后,我注意到SO上已经发布了密切相关的问答。也可以看看:

我可以将Python脚本作为服务运行(在Windows中)吗?怎么样?

如何使Windows知道我用Python编写的服务?



Service Installer无法在64位体系结构上运行,因此选项1成为goto选项。
诺亚·坎贝尔

以上指向ServiceInstaller的链接不再起作用。我在这里找到:sites.google.com/site/conort/...
LarsH

2
没注意到,我认为NT这不一定与名称相反,至少在程序员的演讲中没有。它仅指“ NT 体系结构 ”,而不是“ NT 品牌 ”。就是说,根据Wikipedia上的说法,这有待辩论,因为“这不是Microsoft的正式用语”,但是这种思路仍然有一个传统。
n611x007 2014年

15

逐步说明如何使其工作:

1-首先根据上述基本框架创建一个python文件。并将其保存到例如以下路径:“ 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-在这一步,我们应该注册我们的服务。

管理员身份运行命令提示符,然后键入:

sc创建TestService binpath =“” C:\ Python36 \ Python.exe c:\ PythonFiles \ AppServerSvc.py“ DisplayName =” TestService“ start =自动

binpath的第一个参数是python.exe路径

binpath的第二个参数 是我们已经创建的python文件的路径

不要错过在每个“ = ”符号后加一个空格。

然后,如果一切正常,您应该会看到

[SC] CreateService成功

现在,您的python服务现在已安装为Windows服务。您可以在Service Manager和注册表中的以下位置看到它:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ TestService

3-好吧。您可以在服务管理器上启动服务。

您可以执行提供此服务框架的每个python文件。


有很多的坏榜样在那里如何使用SetEvent(self.hWaitStop)WaitForSingleObject。基于可能是在此处漫无目的地复制了所选答案。这是一种很好的方法,它对于“调试”和“停止”两个参数都可以正常使用。(有关使用SC的部分HandleCommandLine在工作时似乎是多余的,并且可以运行调试。)
Alias_Knagg

3

pysc:Python上的服务控制管理器

作为从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()

创建并启动服务

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

停止并删除服务

import pysc

service_name = 'test_xmlrpc_server'

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

3
有人知道为什么这会引起反对吗?看起来是一个不错的解决方案。
加罗德·切斯尼

3

我开始使用pywin32作为服务托管。

一切都很好,但是我遇到了系统启动时服务无法在30秒内启动的问题(Windows默认超时)。对我来说至关重要,因为Windows启动是在一台物理机上托管的多个虚拟机上同时进行的,并且IO负载很大。错误消息是:

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.

我在pywin上进行了很多工作,但是最终使用了NSSM,正如本答案中所建议的那样。迁移到它非常容易。


2

python中的nssm 3+

(我使用pyinstaller将.py文件转换为.exe )

nssm:如前所述

  • 运行nssm install {ServiceName}
  • 在NSSM的控制台上:

    路径:path \ to \ your \ program.exe

    启动目录:path \ to \ your \#与路径相同,但没有program.exe

    参数:空

如果您不想将项目转换为.exe

  • 创建一个.bat文件 python {{your python.py file name}}
  • 并设置.bat文件的路径

如何提供虚拟环境?
shaik moeed '19

1

使用循环或子线程的完整pywin32示例

经过几天或几天的研究之后,这是我希望找到的答案,使用pywin32使其保持良好状态并自成一体。

这是一个基于循环和一个基于线程的解决方案的完整工作代码。尽管我只测试了2.7和Win7的最新版本,但它可能同时在python 2和python 3上运行。该循环应适合轮询代码,而踏步应该可以处理更多类似于服务器的代码。这似乎与没有标准方法正常关闭的女服务员 wsgi服务器很好地配合。

我也想请注意,似乎有实例负载在那里,就像是几乎是有用的,但在现实中误导,因为他们有剪切和粘贴盲目其他例子。我可能是错的。但是,如果您从不等待,为什么还要创建一个事件?

话虽如此,我仍然觉得我在这里有些不稳定,特别是关于线程版本的退出情况有多干净的问题,但至少我相信这里没有误导之处。

要运行,只需将代码复制到文件中,然后按照说明进行操作即可。

更新:

使用一个简单的标志终止线程。重要的一点是“线程完成”打印。
有关从不合作的服务器线程退出的更详细的示例,请参阅我关于女服务员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()

0

可接受的答案使用win32serviceutil有效,但是很复杂,并且使调试和更改变得更加困难。这是迄今为止更容易使用NSSM(非吸服务管理器)。您编写并舒适地调试了一个普通的python程序,当它最终运行时,您可以在不到一分钟的时间内使用NSSM将其作为服务安装:

在提升的(管理员)命令提示符下运行,nssm.exe install NameOfYourService然后填写以下选项:

  • path:(例如python.exe的路径C:\Python27\Python.exe
  • 参数:(您的python脚本的路径,例如c:\path\to\program.py

顺便说一句,如果您的程序打印了要保留在日志文件中的有用消息,NSSM也可以为您处理这件事,还有更多事情要做。


是的,这是阿德里亚诺答案的重复。我对该答案进行了投票并试图对其进行编辑,但是在进行编辑之后,我正在寻找一个新的答案。
ndemou

如何提供虚拟环境?
shaik moeed '19

0

对于任何想要在VENV或Pycharm中创建服务的人!

阅读完所有的anwsers并创建一些脚本后,如果可以运行python service.py installpython service.py debug,但python service.py start没有响应。

可能是由于venv问题引起的,因为Windows服务通过exec启动您的服务PROJECT\venv\Lib\site-packages\win32\pythonservice.exe

您可以使用powershellcmd测试服务以查找更多错误的详细信息。

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)

如果您遇到类似我的错误,则可以在另一个问题中检查我的答案,我已将其修复并在此处发布代码。


-1

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

  1. 跟进PySvc.py

  2. 更改dll文件夹

我知道这很老了,但是我一直坚持下去。对我来说,通过复制此文件-pywintypes36.dll解决了此特定问题

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

到-> 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. 通过更改路径到python文件夹

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

  1. NET START PySvc
  2. NET STOP PySvc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.