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

Posted

技术标签:

【中文标题】如何在 Windows 中将 Python 脚本作为服务运行?【英文标题】:How do you run a Python script as a service in Windows? 【发布时间】:2010-09-07 03:21:08 【问题描述】:

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

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

是否可以将 Python 程序作为 Windows 服务运行(即无需用户登录即可自动运行)?我不一定要实现这部分,但我需要大致了解如何实现这样做是为了决定是否按照这些思路进行设计。

编辑:感谢到目前为止的所有答案,它们非常全面。我还想知道一件事:Windows 如何知道我的服务?我可以使用本机 Windows 实用程序来管理它吗? 在 /etc/init.d 中放置启动/停止脚本的作用是什么?

【问题讨论】:

【参考方案1】:

是的,你可以。我使用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 方法中设置的标志来中断

【讨论】:

编码后,如何告诉 Windows 将其作为服务运行? @Kit:从命令行使用参数“install”运行脚本。然后您将能够在 Windows 的服务列表中看到您的应用程序,您可以在其中启动、停止它或将其设置为自动启动 python my_script_as_service.py install ? 您特别提到了pythoncom,并在示例代码中导入了它。问题是您从未在示例代码中的任何地方实际使用过 pythoncom,您只需要导入它。为什么要特别提到它而不显示它的用法? 为什么是socket.setdefaulttimeout(60)?它是服务所需要的,还是只是从某些现有服务中意外复制的? :)【参考方案2】:

几乎所有 Windows 可执行文件都可以作为服务安装。

方法一:使用 rktools.exe 中的 instsrv 和 srvany

​​>

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

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

方法 2:使用 Windows NT 的 ServiceInstaller

还有另一种选择,将ServiceInstaller for Windows NT (download-able here) 与python instructions available 结合使用。与名称相反,它也适用于 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

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

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

Can I run a Python script as a service (in Windows)? How?

How do I make Windows aware of a service I have written in Python?

【讨论】:

我刚刚注意到已经有其他类似的问答:***.com/questions/32404/…***.com/questions/34328/… 服务安装程序无法在 64 位架构上运行,因此选项 1 成为 goto 选项。 上面的 ServiceInstaller 链接不再有效。我在这里找到它:sites.google.com/site/conort/… 注解,我认为NT 不一定与名称“相反”,至少在程序员的民间演讲中不会。它只是指“NT architecture”,而不是“NT brand”。也就是说,根据talk on wikipedia 的说法,这是有争议的,因为“这不是微软的官方术语”,但这种思路仍然存在传统。【参考方案3】:

虽然我在几周前对所选答案投了赞成票,但与此同时,我在这个话题上却遇到了很多困难。感觉就像有一个特殊的 Python 安装并使用特殊的模块将脚本作为服务运行是完全错误的方式。便携性之类的呢?

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

我还没有尝试过这个解决方案,但我会立即尝试并在此过程中更新这篇文章。我也对在 Windows 上使用 virtualenvs 感兴趣,所以我迟早会想出一个教程并在这里链接到它。

【讨论】:

运气好吗?我正在为客户端构建一个非常简单的站点,不需要使用整个 Apache 堆栈。正如我从其他 cmets 那里了解到的那样,我自己构建服务听起来也像是自找麻烦。 是的,这很有效,而且很容易做到。您只需提供脚本的路径和参数。我能够在没有控制台的情况下运行我的,以防万一有人以某种方式结束控制台窗口。 虽然这显然可行,但还有其他困难,尤其是当您“不需要使用整个 Apache 堆栈”时:例如 gunicorn 尚未在 Windows 上运行,这实际上是我。 这里的诀窍是将 python.exe 作为服务运行,将你的 python 脚本作为参数运行:比如 "nssm install MyServiceName c:\python27\python.exe c:\temp\myscript.py" 效果很好!在具有多个虚拟环境的系统上,该路径可以引用所需虚拟环境的 Scripts 目录中的 Python 解释器 exe。似乎 PowerShell 中的 new-service 应该能够做到这一点,但是启动(和监控)作为服务的脚本显然涉及更多细节,nssm 很好地处理了这些细节。【参考方案4】:

实现这一点的最简单方法是使用本机命令 sc.exe:

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

参考资料:

    https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx When creating a service with sc.exe how to pass in context parameters?

【讨论】:

我认为,这是您的命令或应用程序本身的问题。无论如何,请检查此support.microsoft.com/en-us/help/886695/… 我的应用程序在服务之外运行良好,我使用上面相同的代码没有结果。 如何提供虚拟环境? 你试过 virtualenv 了吗? 这不起作用。 Windows 服务必须公开 pywin32 包所做的某个接口。然而,一个普通的 Python 脚本是不够的。【参考方案5】:

pysc:Service Control Manager on Python

作为服务运行的示例脚本taken from 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

【讨论】:

有谁知道为什么这会被否决?这看起来是一个不错的解决方案。【参考方案6】:

逐步解释如何使其工作:

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 - 在这一步我们应该注册我们的服务。

administrator 身份运行命令提示符并键入:

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

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

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

不要错过应该在每个“=”符号后放置一个空格。

那么如果一切正常,你应该会看到

[SC] 创建服务成功

现在您的 python 服务已安装为 Windows 服务。您可以在以下服务管理器和注册表中看到它:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TestService

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

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

【讨论】:

有很多关于如何使用SetEvent(self.hWaitStop)WaitForSingleObject 的坏例子。可能是基于此处所选答案的轻率复制。这是一个很好的方法,它对“调试”结束和“停止”参数都有效。 (HandleCommandLine 完成这项工作时,关于使用 SC 的部分似乎是多余的,并且可以运行调试。) 我花了一段时间才意识到,不仅“=”符号后必须有空格,而且符号前不能有空格。【参考方案7】:

最简单的方法是使用:NSSM - Non-Sucking Service Manager。只需下载并解压缩到您选择的位置。它是一个自包含的实用程序,大约 300KB(远小于为此目的而安装整个 pywin32 套件)并且不需要“安装”。 zip 包含该实用程序的 64 位和 32 位版本。两者都应该在当前系统上运行良好(您可以使用 32 位版本来管理 64 位系统上的服务)。

图形化方法

1 - 将 python 程序安装为服务。以管理员身份打开 Win 提示

c:\>nssm.exe install WinService

2 - 在 NSSM 的 GUI 控制台上:

路径:C:\Python27\Python27.exe

启动目录:C:\Python27

参数:c:\WinService.py

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

脚本方法(无 GUI)

如果您的服务应该是自动化、非交互式过程的一部分,这可能会超出您的控制范围,例如批处理或安装程序脚本,这将非常方便。假设这些命令是以管理权限执行的。

为方便起见,此处通过简单地将实用程序称为nssm.exe 来描述命令。但是,建议在脚本中使用其完整路径 c:\path\to\nssm.exe 更明确地引用它,因为它是一个独立的可执行文件,可能位于系统不知道的私有路径中。

1.安装服务

您必须指定服务名称、正确 Python 可执行文件的路径以及脚本的路径:

nssm.exe install ProjectService "c:\path\to\python.exe" "c:\path\to\project\app\main.py"

更明确:

nssm.exe install ProjectService 
nssm.exe set ProjectService Application "c:\path\to\python.exe"
nssm.exe set ProjectService AppParameters "c:\path\to\project\app\main.py"

或者,您可能希望您的 Python 应用程序作为 Python 模块启动。一种简单的方法是告诉 nssm 它需要更改为正确的起始目录,就像您在从命令 shell 启动时所做的那样:

nssm.exe install ProjectService "c:\path\to\python.exe" "-m app.main"
nssm.exe set ProjectService AppDirectory "c:\path\to\project"

此方法适用于虚拟环境和自包含(嵌入式)Python 安装。只需确保使用常用方法正确解决了这些环境中的任何路径问题。 nssm 可以根据需要设置环境变量(例如 PYTHONPATH),还可以启动批处理脚本。

2。启动服务

nssm.exe start ProjectService 

3.停止服务

nssm.exe stop ProjectService

4.要删除服务,请指定confirm 参数以跳过交互式确认。

nssm.exe remove ProjectService confirm

【讨论】:

我以前使用 nssm.exe 将我的 Visual Studio C++ .exe 安装为服务,现在我可以将 nssm.exe 也用于我的 Python .pyc 作为服务。谢谢。 注意:如果您的 *.py 脚本位于带空格的文件夹中(例如:C:\Program Files\myapp.py)需要在引号中指定参数: 参数:“C:\Program文件\myapp.py" 如何提供虚拟环境? 不要浪费更多时间并采用 NSSM 方法。对于虚拟环境,你只需要指向 virtualenv 文件夹中的 python 可执行文件。 @Adriano P 但是当我们想在客户的系统上将我们的应用程序作为服务运行时?在那种情况下如何使用 nssm?【参考方案8】:

我开始使用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 进行了很多斗争,但最终还是使用了 in this answer 提议的 NSSM。迁移到它非常容易。

【讨论】:

【参考方案9】:

使用win32serviceutil 的公认答案有效,但很复杂,使调试和更改更加困难。 更容易使用 NSSM (the Non-Sucking Service Manager)。您编写并舒适地调试一个普通的 python 程序,当它最终工作时,您使用 NSSM 将其作为服务安装在不到一分钟:

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

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

顺便说一句,如果您的程序打印出有用的消息,并且您希望将这些消息保存在日志文件中,那么 NSSM 也可以处理这个问题,并且为您处理更多事情。

【讨论】:

是的,这是阿德里亚诺回答的副本。我赞成该答案并尝试对其进行编辑,但在编辑后我正在寻找一个新答案。 如何提供虚拟环境?【参考方案10】:

nssm 在 python 3+中

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

nssm: 如前所述

运行 nssm install ServiceName

在 NSSM 的控制台上:

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

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

参数:空

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

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

【讨论】:

如何提供虚拟环境?【参考方案11】:

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

在断断续续地工作了几天之后,这是我希望找到的答案,使用 pywin32 让它保持良好和独立。

这是一个基于循环和一个基于线程的解决方案的完整工作代码。 它可能适用于 python 2 和 3,尽管我只在 2.7 和 Win7 上测试了最新版本。循环应该适用于轮询代码,而线程应该适用于更多类似服务器的代码。它似乎可以很好地与waitress wsgi 服务器配合使用,因为它没有标准的优雅关闭方式。

我还想指出,那里似乎有很多示例,例如this,它们几乎有用,但实际上具有误导性,因为他们盲目地剪切和粘贴了其他示例。我可能是错的。但是,如果您从不等待,为什么还要创建一个事件呢?

也就是说,我仍然觉得我在这里有点不稳定,特别是关于线程版本的退出有多干净,但至少我相信这里没有任何误导

要运行,只需将代码复制到文件并按照说明操作。

更新:

使用一个简单的标志来终止线程。重要的是“线程完成”打印。 有关从不合作的服务器线程退出的更详细的示例,请参阅我的 post about the waitress wsgi server。

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

【讨论】:

这是一个后台运行的程序,没有控制台,打印命令在哪里输出消息? 打印出来证明它可以在 CLI 上运行。 IIRC 它们也将在作为带有调试标志的服务运行时显示。【参考方案12】:

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

    跟进 PySvc.py

    更改 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
    更改 python 文件夹的路径

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

    NET START PySvc NET STOP PySvc

【讨论】:

【参考方案13】:

这个答案是来自 *** 上的几个来源的剽窃者 - 其中大部分都在上面,但我忘记了其他的 - 抱歉。这很简单,脚本“按原样”运行。对于测试脚本的版本,然后将其复制到服务器并停止/启动相关服务。它应该适用于所有脚本语言(Python、Perl、node.js),以及 GitBash、PowerShell 等批处理脚本,甚至是旧的 DOS bat 脚本。 pyGlue 是 Windows 服务和脚本之间的粘合剂。

'''
A script to create a Windows Service, which, when started, will run an executable with the specified parameters.
Optionally, you can also specify a startup directory

To use this script you MUST define (in class Service)
1. A name for your service (short - preferably no spaces)
2. A display name for your service (the name visibile in Windows Services)
3. A description for your service (long details visible when you inspect the service in Windows Services)
4. The full path of the executable (usually C:/Python38/python.exe or C:WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
5. The script which Python or PowerShell will run(or specify None if your executable is standalone - in which case you don't need pyGlue)
6. The startup directory (or specify None)
7. Any parameters for your script (or for your executable if you have no script)

NOTE: This does not make a portable script.
The associated '_svc_name.exe' in the dist folder will only work if the executable,
(and any optional startup directory) actually exist in those locations on the target system

Usage: 'pyGlue.exe [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|delayed] : 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
        --perfmondll file: .dll file to use when querying the service for performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
        --wait seconds: Wait for the service to actually start or stop.
                If you specify --wait with the 'stop' option, the service and all dependent services will be stopped,
                each waiting the specified period.
'''

# Import all the modules that make life easy
import servicemanager
import socket
import sys
import win32event
import win32service
import win32serviceutil
import win32evtlogutil
import os
from logging import Formatter, Handler
import logging
import subprocess


# Define the win32api class
class Service (win32serviceutil.ServiceFramework):
        # The following variable are edited by the build.sh script
        _svc_name_ = "TestService"
        _svc_display_name_ = "Test Service"
        _svc_description_ = "Test Running Python Scripts as a Service"
        service_exe = 'c:/Python27/python.exe'
        service_script = None
        service_params = []
        service_startDir = None

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

        # Configure logging to the WINDOWS Event logs
        def configure_logging(self):
                self.formatter = Formatter('%(message)s')
                self.handler = logHandler()
                self.handler.setFormatter(self.formatter)
                self.logger = logging.getLogger()
                self.logger.addHandler(self.handler)
                self.logger.setLevel(logging.INFO)

        # Stop the service
        def SvcStop(self):
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                win32event.SetEvent(self.hWaitStop)

        # Run the service
        def SvcDoRun(self):
                self.main()

        # This is the service
        def main(self):

                # Log that we are starting
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                                          (self._svc_name_, ''))

                # Fire off the real process that does the real work
                logging.info('%s - about to call Popen() to run %s %s %s', self._svc_name_, self.service_exe, self.service_script, self.service_params)
                self.process = subprocess.Popen([self.service_exe, self.service_script] + self.service_params, shell=False, cwd=self.service_startDir)
                logging.info('%s - started process %d', self._svc_name_, self.process.pid)

                # Wait until WINDOWS kills us - retrigger the wait for stop every 60 seconds
                rc = None
                while rc != win32event.WAIT_OBJECT_0:
                        rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))

                # Shut down the real process and exit
                logging.info('%s - is terminating process %d', self._svc_name_, self.process.pid)
                self.process.terminate()
                logging.info('%s - is exiting', self._svc_name_)


class logHandler(Handler):
        '''
Emit a log record to the WINDOWS Event log
        '''

        def emit(self, record):
                servicemanager.LogInfoMsg(record.getMessage())


# The main code
if __name__ == '__main__':
        '''
Create a Windows Service, which, when started, will run an executable with the specified parameters.
        '''

        # Check that configuration contains valid values just in case this service has accidentally
        # been moved to a server where things are in different places
        if not os.path.isfile(Service.service_exe):
                print('Executable file(!s) does not exist'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        if not os.access(Service.service_exe, os.X_OK):
                print('Executable file(!s) is not executable'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        # Check that any optional startup directory exists
        if (Service.service_startDir is not None) and (not os.path.isdir(Service.service_startDir)):
                print('Start up directory(!s) does not exist'.format(Service.service_startDir), file=sys.stderr)
                sys.exit(0)

        if len(sys.argv) == 1:
                servicemanager.Initialize()
                servicemanager.PrepareToHostSingle(Service)
                servicemanager.StartServiceCtrlDispatcher()
        else:
                # install/update/remove/start/stop/restart or debug the service
                # One of those command line options must be specified
                win32serviceutil.HandleCommandLine(Service)

现在进行了一些编辑,您不希望将所有服务都称为“pyGlue”。所以有一个脚本 (build.sh) 可以插入这些位并创建一个自定义的“pyGlue”并创建一个“.exe”。正是这个“.exe”作为 Windows 服务安装。安装后,您可以将其设置为自动运行。

#!/bin/sh
# This script build a Windows Service that will install/start/stop/remove a service that runs a script
# That is, executes Python to run a Python script, or PowerShell to run a PowerShell script, etc

if [ $# -lt 6 ]; then
        echo "Usage: build.sh Name Display Description Executable Script StartupDir [Params]..."
        exit 0
fi

name=$1
display=$2
desc=$3
exe=$4
script=$5
startDir=$6
shift; shift; shift; shift; shift; shift
params=
while [ $# -gt 0 ]; do
        if [ "$params" != "" ]; then
                params="$params, "
        fi
        params="$params'$1'"
        shift
done

cat pyGlue.py | sed -e "s/pyGlue/$name/g" | \
        sed -e "/_svc_name_ =/s?=.*?= '$name'?" | \
        sed -e "/_svc_display_name_ =/s?=.*?= '$display'?" | \
        sed -e "/_svc_description_ =/s?=.*?= '$desc'?" | \
        sed -e "/service_exe =/s?=.*?= '$exe'?" | \
        sed -e "/service_script =/s?=.*?= '$script'?" | \
        sed -e "/service_params =/s?=.*?= [$params]?" | \
        sed -e "/service_startDir =/s?=.*?= '$startDir'?" > $name.py

cxfreeze $name.py --include-modules=win32timezone

安装 - 将“.exe”服务器和脚本复制到指定文件夹。使用“安装”选项以管理员身份运行“.exe”。以管理员身份打开 Windows 服务,然后启动您的服务。对于升级,只需复制新版本的脚本并停止/启动服务。

现在每台服务器都是不同的——不同的 Python 安装,不同的文件夹结构。我为每个服务器维护一个文件夹,其中包含 pyGlue.py 和 build.sh 的副本。我创建了一个“serverBuild.sh”脚本来重建该服务器上的所有服务。

# A script to build all the script based Services on this PC
sh build.sh AutoCode 'AutoCode Medical Documents' 'Autocode Medical Documents to SNOMED_CT and AIHW codes' C:/Python38/python.exe autocode.py C:/Users/russell/Documents/autocoding -S -T

【讨论】:

以上是关于如何在 Windows 中将 Python 脚本作为服务运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Bash 脚本中将 DOS/Windows 换行符 (CRLF) 转换为 Unix 换行符 (LF)

从 C++/Qt 程序在 Windows 中运行 Anaconda 脚本

如何在 python 脚本中将 .obj 文件从搅拌机导入搅拌机游戏引擎

在 Eclipse(Pydev) 中将命令行参数传递给 Python 脚本

如何设置 Python 脚本以在 Apache 2.0 中工作?

如何在 Windows 中将 ghostscript DLL 库提供给 python?