Appearance
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: 54. 实战案例:注册 Minio 文件服务器
以 Minio 为例,展示如何快速部署:
文件布局:
D:\Tools\minio\minio.exe(主程序)D:\Tools\minio\minio-server.exe(重命名后的 WinSW)D:\Tools\minio\minio-server.xml(配置文件)
执行安装:
bashcd 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 最佳实践建议
- 最小权限原则:默认
LocalSystem权限过大。如果服务只需读写特定目录,建议在<serviceaccount>中配置专门的普通用户。 - WinSW v3 新特性:WinSW v3 开始支持 YAML 配置格式,语法更简洁,且支持更加灵活的日志插件。
- 依赖管理:如果你的服务依赖其他服务(如 MySQL),可以使用
<depend>MySQL</depend>确保启动顺序。 - 交互式服务:虽然 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 程序,建议使用“启动”文件夹或计划任务。
