Skip to content

WinSW:将程序注册为 Windows 服务实践 ​

在 Windows 服务器运维中,经常需要将控制台程序(如 frpc.exe、java -jar 等)以后台服务的形式运行,以实现开机自启和故障自动重启。WinSW (Windows Service Wrapper) 是目前最简单且功能强大的解决方案。

1. WinSW 简介与优势 ​

1.1 为什么选择 WinSW? ​

在 Windows 环境下,原生将控制台程序注册为服务的方式(如 sc create)往往难以处理带有参数的命令,且缺乏自动重启机制。

工具深度对比:

维度sc.exe (系统原生)NSSM (Non-Sucking)WinSW (推荐)
配置方式命令行参数,极其繁琐GUI 图形界面 (不便自动化)XML/YAML 文本配置
故障重启有限支持支持支持 (多级延迟重启)
日志轮转不支持仅重定向,无轮转内置多种轮转模式
环境变量需手动修改注册表支持支持 (XML 标签直观配置)
活跃度静态已停更多年持续维护 (支持 v3 版本)

2. 安装与快速上手 ​

2. 核心配置模板 ​

以下是一个典型的配置示例,包含自动重启和日志管理逻辑:

xml
<service>
  <!-- 该服务的唯一标识 -->
  <id>myservice</id>
  <!-- 该服务的显示名称 -->
  <name>My Awesome Service</name>
  <!-- 该服务的描述 -->
  <description>这是一个使用 WinSW 包装的自定义服务。</description>

  <!-- 要运行的程序路径(绝对路径) -->
  <executable>C:\Path\To\YourApp.exe</executable>

  <!-- 携带的启动参数 -->
  <arguments>-c config.yaml --port 8080</arguments>

  <!-- 故障恢复策略 -->
  <onfailure action="restart" delay="10 sec"/>
  <onfailure action="restart" delay="60 sec"/>

  <!-- 日志模式:append 表示追加,reset 表示覆盖 -->
  <logmode>append</logmode>
  <logpath>C:\Path\To\Logs</logpath>
</service>

3. 核心配置详解 ​

3.1 完整 XML 标签参考 ​

xml
<service>
  <!-- 服务的唯一标识,在系统中不可重复 -->
  <id>myservice</id>
  <name>My Awesome Service</name>
  <description>这是服务的描述信息。</description>

  <!-- %BASE% 指向 myservice.exe 所在的目录 -->
  <executable>%BASE%\minio.exe</executable>

  <!-- 启动参数 -->
  <arguments>server "%BASE%\data"</arguments>

  <!-- 设置环境变量 -->
  <env name="HOME" value="%BASE%"/>
  <env name="MINIO_ROOT_USER" value="admin"/>

  <!-- 工作目录 -->
  <workingdirectory>%BASE%</workingdirectory>

  <!-- 优先级:Idle, BelowNormal, Normal, AboveNormal, High, RealTime -->
  <priority>Normal</priority>

  <!-- 停止服务的方式:默认是直接 Kill -->
  <stopparentprocessfirst>true</stopparentprocessfirst>
  <stoptimeout>10sec</stoptimeout>
</service>

3.2 高级日志配置 (Log Rotation) ​

对于生产环境,必须配置日志轮转以防止磁盘被撑爆。

xml
<log mode="roll-by-size-time">
  <!-- 单个日志文件最大 10MB -->
  <sizeThreshold>10240</sizeThreshold>
  <!-- 时间格式 -->
  <pattern>yyyyMMdd</pattern>
  <!-- 每天凌晨自动切分 -->
  <autoRollAtTime>00:00:00</autoRollAtTime>
  <!-- 自动清理 5 天前的日志并压缩 -->
  <zipOlderThanNumDays>5</zipOlderThanNumDays>
  <zipDateFormat>yyyyMMdd</zipDateFormat>
</log>

日志清理策略:

  • keepFiles: 仅保留最近的 N 个日志文件。
  • zipOlderThanNumDays: 自动将旧日志打包为 ZIP,极大地节省磁盘空间。

3.3 WinSW v3 YAML 配置示例 ​

WinSW v3 引入了 YAML 支持,使得配置更加直观:

yaml
id: my-node-app
name: Node.js Service
description: This is a node.js application service.
executable: node
arguments: app.js
env:
  - name: NODE_ENV
    value: production
log:
  mode: roll
  sizeThreshold: 10mb
  keepFiles: 5

4. 实战案例:注册 Minio 文件服务器 ​

以 Minio 为例,展示如何快速部署:

  1. 文件布局:

    • D:\Tools\minio\minio.exe (主程序)
    • D:\Tools\minio\minio-server.exe (重命名后的 WinSW)
    • D:\Tools\minio\minio-server.xml (配置文件)
  2. 执行安装:

    bash
    cd D:\Tools\minio
    minio-server.exe install
    minio-server.exe start

5. 运维指令全集 ​

必须以管理员权限运行终端。除了基础的增删改查,WinSW 还支持许多高级指令:

指令描述
install将服务安装到 Windows 系统服务列表中
uninstall停止并彻底移除服务
start启动已安装的服务
stop停止正在运行的服务
restart重新启动服务
status检查服务的当前状态(Started, Stopped 等)
refresh在不重新安装的情况下刷新服务属性(某些系统版本支持)
help显示帮助信息

6. 深度排障与最佳实践 ​

6.1 以 frp 服务无法启动为例 ​

在注册类似 frp 的内网穿透工具时,经常遇到“服务已安装但无法启动”的问题。

解决方案:参数分行 WinSW 在处理某些带有复杂路径或短横线的参数时,推荐使用 <startargument> 标签分行书写。

xml
<service>
    <id>frp</id>
    <executable>C:\Tools\frp\frpc.exe</executable>
    <startargument>-c</startargument>
    <startargument>C:\Tools\frp\frpc.ini</startargument>
    <logmode>append</logmode>
</service>

6.2 常见错误代码与含义 ​

如果在事件查看器中看到以下退出代码,可以参考排障:

  • 0: 正常退出。
  • 1: 通用错误,通常是路径不对或参数非法。
  • 2: 系统找不到指定的文件(Check <executable> 路径)。
  • 5: 拒绝访问(请确保以管理员身份运行,或检查文件夹权限)。
  • 1067: 进程意外终止(通常是程序内部崩溃,查看程序的 stdout.log)。

6.3 最佳实践建议 ​

  1. 最小权限原则:默认 LocalSystem 权限过大。如果服务只需读写特定目录,建议在 <serviceaccount> 中配置专门的普通用户。
  2. WinSW v3 新特性:WinSW v3 开始支持 YAML 配置格式,语法更简洁,且支持更加灵活的日志插件。
  3. 依赖管理:如果你的服务依赖其他服务(如 MySQL),可以使用 <depend>MySQL</depend> 确保启动顺序。
  4. 交互式服务:虽然 Windows 逐渐限制服务与桌面交互,但 WinSW 仍支持通过 <interactive/> 标签尝试开启(不推荐用于生产)。

7. 常见问题 (FAQ) ​

Q: 如何在安装后修改配置? A: 修改 XML 文件后,必须先执行 myservice.exe stop,然后执行 myservice.exe uninstall,再执行 myservice.exe install 和 start。直接修改 XML 不会自动生效。

Q: 服务启动后立即停止,日志里没报错怎么办? A: 尝试直接在命令行运行 <executable> 中的命令(带上参数),观察是否有环境缺失或报错。通常是因为 WinSW 运行时缺少的环境变量在你的用户环境下是存在的。

Q: WinSW 支持多服务部署在同一个目录吗? A: 可以,但必须确保每个服务都有独立的可执行包装器(例如 svc1.exe, svc2.exe)和对应的 XML 文件,且 id 必须唯一。

Q: 如何在服务中配置 .NET Core 的端口? A: 可以通过 <env name="ASPNETCORE_URLS" value="http://*:5000"/> 或者在 <arguments> 中传递 --urls http://*:5000。

Q: WinSW 能管理 GUI 程序吗? A: 虽然技术上可以,但 Windows 服务运行在 Session 0 中,无法显示界面。如果是需要桌面的 GUI 程序,建议使用“启动”文件夹或计划任务。


8. 参考资料 ​

方舟 · Ark