在 Windows Server 环境下,让 Java 应用以“服务”(Service)形式后台运行,核心痛点在于解决两个问题:
- 无头运行:不依赖用户登录,开机自启。
- 会话隔离:避免因为 RDP 断开或控制台关闭导致进程被杀(这是直接用
java -jar启动最常见的坑)。
目前业界最主流、稳定且无需额外安装重型中间件方案主要有两种:WinSW (Windows Service Wrapper) 和 NSSM。其中 WinSW 是近年来更受推崇的方案,尤其是配合现代 CI/CD 流程时。
下面我按推荐程度从高到低,给出具体实操步骤。
方案一:使用 WinSW (Windows Service Wrapper) —— 首选推荐
WinSW 是一个轻量级的 .NET 工具,专门用于将任何可执行文件包装为 Windows 服务。它配置简单,支持 XML 配置,且官方维护活跃。
1. 下载与准备
去 GitHub 的 WinSW Releases 页面下载最新版本的 WinSW-x64.exe(根据你的服务器架构选择 x64 或 x86,现在基本都是 x64)。
假设你的 Java 应用目录结构如下:
D:myapp
├── app.jar # 你的 Java 包
├── jre # 如果自带 JRE
└── win-service.xml # 即将创建的配置文件
将下载的 WinSW-x64.exe 重命名为 win-service.xml 同名的可执行文件名,比如叫 MyAppSvc.exe。
注意:WinSW 要求可执行文件的名称必须与配置文件同名(去掉 .exe 后缀后一致)。所以这里我们创建 MyAppSvc.exe 和 MyAppSvc.xml。
2. 编写配置文件 (MyAppSvc.xml)
在 D:myapp 目录下创建 MyAppSvc.xml,内容如下:
<service>
<id>MyJavaApp</id>
<name>My Java Application Service</name>
<description>This is a long description for the My Java Application service.</description>
<!-- 指定 JVM 路径,如果没有独立 JRE,直接写 java -->
<executable>D:myappjrebinjava.exe</executable>
<!-- 启动参数 -->
<arguments>-Xms256m -Xmx512m -jar D:myappapp.jar --spring.profiles.active=prod</arguments>
<!-- 日志处理:WinSW 默认会捕获 stdout/stderr 并写入日志文件 -->
<log mode="roll"></log>
<!-- 可选:设置工作目录 -->
<workingdirectory>D:myapp</workingdirectory>
</service>
<id>: 服务唯一标识,不能有空格。<executable>: Java 解释器路径。强烈建议使用应用自带的 JRE,而不是系统环境变量里的 Java,以避免版本冲突和环境差异。<arguments>: 常规 Java 启动参数。<log>: 配置日志滚动策略,方便排查问题。
3. 注册并启动服务
打开命令提示符(CMD)或 PowerShell,以管理员身份运行,进入 D:myapp 目录:
# 注册服务
MyAppSvc.exe install
# 启动服务
MyAppSvc.exe start
# 查看状态
MyAppSvc.exe status
此时,你可以在 services.msc(服务管理器)中看到名为 “My Java Application Service” 的服务。它会在开机时自动启动,即使你没有登录 Windows,它也在后台运行。
4. 常用管理命令
MyAppSvc.exe stop # 停止服务
MyAppSvc.exe uninstall # 卸载服务(清理注册表残留)
MyAppSvc.exe restart # 重启服务
方案二:使用 NSSM (Non-Sucking Service Manager)
NSSM 是一个老牌的、基于 C++ 编写的工具,功能比 WinSW 更底层一些,适合对进程控制有极致要求的场景。
1. 下载
前往 NSSM 官网 下载对应版本的 zip 包,解压后将 nssm.exe 复制到你的应用目录或系统 PATH 中。
2. 图形化配置(推荐新手)
- 以管理员身份运行 CMD。
- 输入
nssm install MyJavaApp。 - 弹出 GUI 窗口:
- Path: 填写
java.exe的完整路径。 - Startup directory: 填写
D:myapp。 - Arguments: 填写
-Xms256m -Xmx512m -jar D:myappapp.jar。 - 其他选项保持默认即可,点击 “Install service”。
- Path: 填写
3. 命令行配置(适合脚本化部署)
如果你不想用 GUI,也可以用命令行:
nssm install MyJavaApp "D:myappjrebinjava.exe" "-Xms256m -Xmx512m -jar D:myappapp.jar"
nssm set MyJavaApp AppDirectory "D:myapp"
nssm set MyJavaApp Start SERVICE_AUTO_START
nssm start MyJavaApp
关键注意事项与避坑指南
-
不要使用
start /b java -jar ...
很多教程会教你用批处理脚本加start /b来隐藏黑框。这在本地测试没问题,但在生产环境极不稳定。一旦 RDP 会话结束,或者用户注销,这个进程可能会因为失去控制台句柄而异常退出,或者无法真正作为 Windows 服务被监控系统管理。 -
JVM 内存泄漏与 OOM
在服务模式下,Java 进程的 PID 是固定的。确保你的-Xms和-Xmx设置合理。如果发生 OutOfMemoryError,Java 进程会崩溃,但 Windows 服务不会自动重启。你需要配合监控工具(如 Prometheus + Node Exporter,或简单的 Health Check 脚本)来实现故障自愈。 -
权限问题
Windows 服务默认以Local System账户运行,拥有最高权限。如果你的 Java 应用需要读写特定目录(如/var/log对应的 Windows 路径),请确保该目录对SYSTEM或你指定的服务账户有读写权限。建议在配置文件中通过<workingdirectory>明确指定工作目录,避免路径解析错误。 -
端口占用检测
在<arguments>中,可以加上一些检查逻辑,但更推荐的做法是在 Java 应用内部实现健康检查端点(Health Endpoint),并通过 Windows Task Scheduler 或第三方监控XX定期访问该端点。如果返回非 200,则执行net stop MyAppSvc && net start MyAppSvc。 -
日志轮转
WinSW 和 NSSM 都会捕获标准输出(stdout)和标准错误(stderr)。如果 Java 应用本身也使用 Logback/Log4j 输出文件日志,请注意区分。通常建议 Java 应用只输出到文件,而将控制台日志交给服务管理器处理,便于集中收集。
总结
对于大多数国内企业级 Java 后端应用,WinSW 是最佳选择。它配置简洁、XML 结构化清晰、易于集成到 Ansible/Puppet/Chef 等自动化运维工具中。NSSM 作为备选,适用于需要更精细进程控制(如信号处理)的场景。
避免使用传统的 cygwin 或 procrun(Tomcat 内置的那种),除非你有遗留系统依赖。现代 Windows Server 环境完全可以用轻量级 wrapper 解决 Java 服务化问题。
云技术博客