杰瑞科技汇

Windows 服务如何用 Python 开发与部署?

什么是 Windows 服务?

Windows 服务(在早期版本中也称为 NT 服务)是一种在后台运行的应用程序,它没有用户界面,并且可以在用户登录之前启动,它们通常用于需要长期运行、自动启动或在特定系统事件(如系统启动时)执行任务的应用程序,

Windows 服务如何用 Python 开发与部署?-图1
(图片来源网络,侵删)
  • 数据库服务器
  • Web 服务器
  • 定时任务调度器
  • 文件监控服务
  • 系统监控工具

核心概念

在创建 Python 服务之前,你需要了解几个核心概念:

  • 服务名称: 一个唯一的名称,用于在系统中标识你的服务(MyPythonService)。
  • 显示名称: 用户在服务管理器中看到的友好名称("My Awesome Python Service")。
  • 描述: 对服务功能的详细描述。
  • 启动类型:
    • 自动: Windows 启动时自动运行。
    • 手动: 需要手动启动或通过其他程序触发。
    • 禁用: 服务被禁用,无法启动。
  • 服务账户: 服务以哪个用户的身份运行。
    • LocalSystem: 功能最强大的本地系统账户,有很高的权限。
    • LocalService: 具有较低权限的本地账户,用于访问网络资源。
    • NetworkService: 与 LocalService 类似,但更侧重于网络访问。
    • 特定用户账户:使用指定的用户名和密码运行。
  • 事件日志: 服务通过 Windows 事件日志来记录运行状态、错误和信息,这对于调试至关重要。

实现方法

主要有两种流行的方式来创建 Python Windows 服务:

  1. 使用第三方库(推荐): 这是最简单、最快捷的方式,库已经为你处理了所有与 Windows API 交互的复杂工作。
  2. 使用原生 Python 模块 (pywin32): 提供了更底层的控制,但代码更复杂。

使用 pywin32

这是最经典和最直接的方法,它直接调用 Windows API。

步骤 1: 安装 pywin32

pip install pywin32

步骤 2: 编写 Python 脚本

创建一个名为 my_service.py 的文件,这个脚本需要包含一个主类,继承自 win32serviceutil.ServiceFramework

Windows 服务如何用 Python 开发与部署?-图2
(图片来源网络,侵删)
# my_service.py
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import time
import logging
# --- 配置你的服务 ---
SERVICE_NAME = "MyPythonService"
SERVICE_DISPLAY_NAME = "My Awesome Python Service"
SERVICE_DESCRIPTION = "This is a sample Python service that runs in the background."
class MyPythonService(win32serviceutil.ServiceFramework):
    """
    Python 服务示例
    """
    _svc_name_ = SERVICE_NAME
    _svc_display_name_ = SERVICE_DISPLAY_NAME
    _svc_description_ = SERVICE_DESCRIPTION
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        # 在这里初始化你的应用
        self.logger = self._setup_logger()
        self.logger.info(f"'{self._svc_display_name_}' 服务正在启动...")
    def _setup_logger(self):
        """配置日志记录到 Windows 事件日志"""
        logger = logging.getLogger(SERVICE_NAME)
        logger.setLevel(logging.INFO)
        # 创建一个 handler 来写入 Windows 事件日志
        handler = servicemanager.EventLogHandler()
        formatter = logging.Formatter(f'%(asctime)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        return logger
    def SvcStop(self):
        """停止服务"""
        self.logger.info("服务停止请求已收到。")
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
    def SvcDoRun(self):
        """服务的主运行循环"""
        self.logger.info("服务主循环已启动。")
        # 在这里放置你的主要服务逻辑
        try:
            while True:
                # 模拟工作
                self.logger.info("服务正在运行...")
                time.sleep(5)
                # 检查停止事件
                if win32event.WaitForSingleObject(self.hWaitStop, 0) == win32event.WAIT_OBJECT_0:
                    break
        except Exception as e:
            self.logger.error(f"服务运行时发生错误: {e}", exc_info=True)
        finally:
            self.logger.info("服务已停止。")
if __name__ == '__main__':
    # 如果直接运行此脚本,则安装服务
    if len(sys.argv) == 1:
        # 安装服务
        win32serviceutil.HandleCommandLine(MyPythonService)
    else:
        # 如果由服务控制管理器启动,则运行服务
        win32serviceutil.ServiceFramework.__init__(sys.modules[__name__], sys.argv)

步骤 3: 安装服务

管理员身份打开命令提示符 (CMD) 或 PowerShell,然后运行你的脚本:

python my_service.py install

这会执行 win32serviceutil.HandleCommandLine 中的 install 命令,将你的服务注册到 Windows 服务管理器中。

步骤 4: 启动和管理服务

你可以使用标准的 Windows 命令或服务管理器来控制你的服务。

命令行方式 (管理员 CMD/PowerShell):

Windows 服务如何用 Python 开发与部署?-图3
(图片来源网络,侵删)
  • 启动服务:

    net start MyPythonService

    或者使用 sc 命令:

    sc start MyPythonService
  • 停止服务:

    net stop MyPythonService

    或者:

    sc stop MyPythonService
  • 删除服务:

    python my_service.py remove

    或者使用 sc:

    sc delete MyPythonService

图形界面方式:

  1. Win + R,输入 services.msc 并回车。
  2. 在服务列表中找到 "My Awesome Python Service"。
  3. 你可以右键点击它来启动、停止、暂停或重新启动。
  4. 双击它可以查看属性,修改启动类型(自动/手动/禁用)或查看事件日志。

使用 pywin32-service 库 (更现代的封装)

这个库是对 pywin32 的一个更高级的封装,语法更简洁,推荐用于新项目。

步骤 1: 安装 pywin32-service

pip install pywin32-service

步骤 2: 编写 Python 脚本

创建一个名为 modern_service.py 的文件。

# modern_service.py
import win32serviceutil
import win32service
import win32event
import servicemanager
import time
import logging
import sys
# --- 配置你的服务 ---
SERVICE_NAME = "MyModernPythonService"
SERVICE_DISPLAY_NAME = "My Modern Python Service"
SERVICE_DESCRIPTION = "A more modern way to create a Python service."
class ModernPythonService(win32serviceutil.ServiceFramework):
    _svc_name_ = SERVICE_NAME
    _svc_display_name_ = SERVICE_DISPLAY_NAME
    _svc_description_ = SERVICE_DESCRIPTION
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.logger = self._setup_logger()
        self.logger.info(f"'{self._svc_display_name_}' 正在启动...")
    def _setup_logger(self):
        """配置日志记录到 Windows 事件日志"""
        logger = logging.getLogger(SERVICE_NAME)
        logger.setLevel(logging.INFO)
        # 使用 servicemanager 提供的日志处理器
        handler = servicemanager.EventLogHandler()
        formatter = logging.Formatter('%(message)s') # 简化格式
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        return logger
    def SvcStop(self):
        """停止服务"""
        self.logger.info("停止请求已收到。")
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
    def SvcDoRun(self):
        """服务的主运行循环"""
        self.logger.info("主循环已启动。")
        while True:
            # 检查停止事件
            result = win32event.WaitForSingleObject(self.hWaitStop, 5000) # 等待5秒
            if result == win32event.WAIT_OBJECT_0:
                self.logger.info("停止事件被触发,正在退出循环。")
                break
            # 你的服务逻辑
            self.logger.info("服务正在执行任务...")
            # time.sleep(5) # 循环内部已经通过 WaitForSingleObject 实现了等待
if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(ModernPythonService)

安装和运行方式与 pywin32 完全相同。


最佳实践和注意事项

  1. 日志记录是关键: 服务没有控制台,所有调试信息、错误和状态都必须通过 Windows 事件日志 记录,务必实现可靠的日志记录。
  2. 处理异常: 代码中的任何未捕获异常都会导致服务崩溃,使用 try...except 块捕获所有异常,并记录到事件日志中。
  3. 避免阻塞操作: SvcDoRun 方法中的主循环不应是无限循环,更好的做法是使用 while not self.stopped: 这样的结构,并在 SvcStop 中设置一个标志。pywin32WaitForSingleObject 是处理停止事件的推荐方式。
  4. 安装依赖: 如果你的服务依赖其他 Python 包,必须确保这些包在服务运行的环境中可用,最佳实践是创建一个虚拟环境,然后在该环境中安装所有依赖,并确保 Python 解释器路径正确。
  5. 以正确的账户运行: 出于安全考虑,尽量避免使用 LocalSystem 账户,如果不需要高权限,使用 LocalServiceNetworkService,如果需要访问网络资源,NetworkService 通常是更好的选择。
  6. 调试服务:
    • 最简单的方法: 在开发阶段,不要立即安装为服务,你可以直接运行你的 Python 脚本 (python my_service.py) 来测试核心逻辑。
    • 使用日志: 当服务运行时,通过 services.msc 查看事件日志,这是了解服务状态的主要方式。
    • 高级调试: 你可以使用 pywin32win32serviceutil.QueryServiceStatussc query 命令来检查服务的当前状态。
特性 pywin32 (原生) pywin32-service (封装)
易用性 中等,需要更多样板代码 ,语法更简洁
控制力 ,直接调用 API 中等,封装了底层细节
推荐度 适合学习原理,或需要高度定制 强烈推荐,适合大多数项目
安装 pip install pywin32 pip install pywin32-service

对于绝大多数 Python 使用 pywin32-service 库是创建 Windows 服务的最佳选择,因为它在易用性和功能性之间取得了很好的平衡,日志记录和异常处理是编写稳定服务的基石。

分享:
扫描分享到社交APP
上一篇
下一篇