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

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

是否有可能运行Python程序作为Windows服务(即自动运行,无需用户登录)?我不一定要实现这一部分,但我需要一个大致的想法,以便决定是否沿着这些路线进行设计。

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


当前回答

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

跟踪PySvc.py 更改DLL文件夹

我知道这很旧了,但我一直都困在这上面。对我来说,这个特定的问题是通过复制这个文件pywintypes36.dll解决的

号码

To -> 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

其他回答

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

在断断续续地研究了几天之后,这里是我希望找到的答案,使用pywin32来保持它的良好和自包含。

这是一个基于循环和一个基于线程的解决方案的完整工作代码。 它可以在python 2和3上运行,尽管我只在2.7和Win7上测试了最新版本。循环应该适用于轮询代码,而tread应该适用于更类似于服务器的代码。它似乎可以很好地与没有标准方法来优雅地关闭的女招待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()

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

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

错误1053:服务没有及时响应启动或控制请求。

错误7009:等待<ServiceName>服务连接的超时(30000毫秒)。

我与pywin进行了大量的斗争,但最终使用了这个答案中提出的NSSM。移居到那里很容易。

NSSM在python 3+

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

nssm: 如前所述

运行nssm install {ServiceName} 在NSSM的控制台: \ \你\ program.exe路径:路径 启动目录:路径\to\your\ #与路径相同,但没有你的program.exe 参数:空

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

用python{{你的python.py文件名}}创建一个.bat文件 并将路径设置为。bat文件

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

GUI的方法

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

一步一步地解释如何使它工作:

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)

在这一步,我们应该注册我们的服务。

以管理员身份运行命令提示符,输入as:

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

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

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

不要忘记在每个“=”号后面加一个空格。

如果一切正常,你应该能看到

[SC] CreateService SUCCESS . [SC

现在你的python服务已经被安装为windows服务了。你可以在服务管理器和注册表中看到它:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ \ TestService服务

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

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