【问题标题】:Running Python Program as Windows Service with a specific conda virtual environment使用特定 conda 虚拟环境将 Python 程序作为 Windows 服务运行
【发布时间】:2019-09-12 17:44:28
【问题描述】:

我正在尝试将使用 Anaconda 编写的 python 程序作为 Windows 服务运行。复杂性在于我想从特定的 conda 虚拟环境运行 Windows 服务。这个想法是,在未来,我们可能会开发更多基于 python 的 Windows 服务,这些服务可能具有不同的模块依赖关系,因此将每个服务保持在自己的虚拟环境中将是理想的。

我找到了几篇关于如何将 Python 程序编写为 Windows 服务的优秀文章,它们运行良好。我创建了一个非常简单的测试程序,它只是在服务启动后将一些消息写入文本文件。我可以成功地将这个测试 python 程序安装为 Windows 服务,并且在我的文件中看到各种文本消息。但是,当我尝试将 Numpy 或 TensorFlow 等模块导入到我的简单测试 python 程序中时,该服务将无法启动,并且我收到无法找到它们各自的 DLL 的失败消息。

我确定问题是因为尚未激活所需的 conda 虚拟环境。同时,我尝试在系统级别复制各种 conda 环境变量;尝试将所有必需的 python 库路径从虚拟环境添加到系统路径和系统范围的 python 路径,但无济于事。

我怀疑如果我可以激活 conda 虚拟环境作为我的 python 代码的一部分,那将解决问题。 (我还怀疑将所有必需的模块安装到我的基本配置中会解决问题,但我想避免这种情况)。

这是我编写的小测试程序。该程序可以很好地与基本的 Python 模块(如 sys、os 等)配合使用。当我尝试运行它并包含 Numpy 或 TensorFlow 时,它失败并显示以下错误消息: (这是在我尝试启动我的服务后来自 Windows 事件查看器 - 它确实安装正确):

Python 无法导入服务的模块 回溯(最近一次通话最后): 文件“D:\TFS\Projects\DEV\AEPEnrollmentForms\src\aepenrl\Windows_Service_Example.py”,第 35 行,在 将 numpy 导入为 np 文件“C:\Users\pboerner\AppData\Local\conda\conda\envs\aepenr\lib\site-packages\numpy__init__.py”,第 140 行,在 从 。导入_distributor_init 文件“C:\Users\pboerner\AppData\Local\conda\conda\envs\aepenr\lib\site-packages\numpy_distributor_init.py”,第 34 行,在 从 。导入 _mklinit ImportError:DLL 加载失败:找不到指定的模块。 %2:%3

这是简单测试程序的代码。 (我从 Davide Mastromatteo 提供的一篇优秀文章中获取的大部分 Windows 服务集成工作)

import numpy as np

import socket
import sys
import time

import win32serviceutil

import servicemanager
import win32event
import win32service


class SimpleService(win32serviceutil.ServiceFramework):
    '''Base class to create winservice in Python'''

    _svc_name_ = 'TestPythonSrvc'
    _svc_display_name_ = 'Test Python Service'
    _svc_description_ = 'Test to see how to create a windows service with python'

    @classmethod
    def parse_command_line(cls):
        '''
        ClassMethod to parse the command line
        '''
        win32serviceutil.HandleCommandLine(cls)

    def __init__(self, args):
        '''
        Constructor of the winservice
        '''
        self.isrunning=True
        self.fid = open("D:\\temp\\simple_service.txt", "w")
        self.fid.write("Initialize\n")
        self.fid.flush()

        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        '''
        Called when the service is asked to stop
        '''
        self.stop()
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        '''
        Called when the service is asked to start
        '''
        self.start()
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ''))
        self.main()

    def start(self):
        '''
        Override to add logic before the start
        eg. running condition
        '''
        self.isrunning = True
        self.fid.write("Start method called\n")
        self.fid.flush()

    def stop(self):
        '''
        Override to add logic before the stop
        eg. invalidating running condition
        '''
        self.isrunning = False
        self.fid.write("STOP method called. Setting stop flag\n")
        self.fid.flush()

    def main(self):
        '''
        Main class to be ovverridden to add logic
        '''
        a = np.zeros((100,1))
        while True:
            if self.isrunning:
                self.fid.write(f"Tick. Numpy array shape {a.shape}\n")
                self.fid.flush()
                time.sleep(1)
            else:
                self.fid.write("Breaking out of main loop\n")
                self.fid.flush()
                break;

        self.fid.write("Closing the log file\n")
        self.fid.flush()
        self.fid.close()

if __name__ == '__main__':
    # This code block was required to get this simple service example to run
    # on a Windows 10 laptop with Admin privs.  Only calling the 
    # HandleCommandLine method alone didn'd seem to work. Not sure why but this
    # code was provided as a solution on the Web.
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(SimpleService)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(SimpleService)

【问题讨论】:

  • 还有一点:非常感谢您的帮助或指导!
  • 您是否尝试在虚拟环境中构建为 python 脚本文件,然后转换为可执行文件,然后将所有库和依赖项与此可执行文件一起运行,然后作为 Windows 服务运行跨度>
  • 我也有类似的问题。我也认为这是由于缺乏激活康达。奇怪的是,我曾经能够通过调用 conda env 的 python.exe 及其绝对路径来启动 python 代码,而无需事先激活 env。后来这发生了变化(4.6?),我通过创建一个批处理脚本来解决这个问题,该脚本首先激活 env,然后调用 python。但是,当您想将 python 代码作为服务运行时,这不起作用。
  • 顺便说一句,我使用 NSSM 快速将 python 代码设置为 Windows 服务。效果很好。不过问题是一样的,因为 conda (4.6) 你必须在运行 python 之前激活你的 conda env,否则它将找不到必要的 DLL。如果您在服务停止时不需要运行任何代码(也就是说,被杀死就可以了)上面提到的解决方法可以正常工作。
  • 这个有运气吗?我面临同样的问题,我认为您认为激活是其根本原因是正确的。很高兴听到一些更新。谢谢

标签: python windows-services


【解决方案1】:

不幸的是,Windows 的服务管理器不如 systemd 灵活。我发现的唯一方法是执行以下操作:

  • 制作一个包含所有逻辑的批处理文件,例如
    call C:\ProgramData\Anaconda3\Scripts\activate.bat C:\ProgramData\Anaconda3
    call activate yourenv
    cd C:/path/to/your/wd
    python yourservice.py and your args
    
    注意:您的 activate.bat 文件可能位于您的主文件夹下:~\AppData\local\Continuum\anaconda3\Scripts
  • 使用 NSSM(恰当地命名):http://nssm.cc/download;另见Run batch file as a Windows service。在调用 nssm 之前,您需要以管理员身份启动命令提示符或 powershell。

这可以很好地服务于bokeh 服务器(使用bokeh serve 而不是python)。我想它适用于任何复杂的python脚本。

您可以在“钩子”选项卡中停止或退出时运行命令。

在我的例子中,批处理文件中的逻辑取决于机器,所以我需要制作一个额外的 python 脚本,在设置时调用它来编写批处理文件。

【讨论】:

    【解决方案2】:

    当我看到一个博客(下面的链接)并按照其中的步骤操作时,我陷入了完全相同的情况

    1. 在您的工作目录中创建一个 run.bat 文件,其中包含以下几行。

      call conda activate env  
      call python app.py
      

      这样当您调用批处理文件时,它会首先激活虚拟环境,然后运行 ​​python 脚本。

    2. 下载NSSM,无需安装。下载后,转到 NSSM -> win32/win64(根据您的计算机体系结构)并以管理员身份在命令提示符下运行以下命令:

      C:\nssm-2.24\win64\nssm install <service name> "C:\path\to\your\run.bat"
      
    3. 此时您的服务已安装但尚未启动。

    4. 您可能需要配置它

      C:\nssm-2.24\win64\nssm edit <service name>
      
    5. 执行上述命令后会打开NSSM服务编辑器,您可以在其中设置显示名称、描述、日志文件等。

    6. 完成后,您可以启动服务

      C:\nssm-2.24\win64\nssm start <service name>
      

    参考链接:- Run Windows Service in its own Python virtual environment

    我只是按照这些步骤操作,它对我来说效果很好。我希望它也对你有用。

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • @coreuter 感谢您的指导。添加了上面的基本部分^
    猜你喜欢
    • 1970-01-01
    • 2016-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    相关资源
    最近更新 更多