#Requires -Version 5.1
|
<#
|
.SYNOPSIS
|
JNPF 微服务批量停止脚本(Windows / PowerShell 版)
|
|
.DESCRIPTION
|
按 start-all.ps1 写入的 .pids/*.pid 文件停止服务。
|
不依赖 Java、Maven 或 Docker,适合在另一个终端执行。
|
|
用法:
|
.\stop-all.ps1 停止全部服务
|
.\stop-all.ps1 jnpf-lims 只停止 jnpf-lims
|
.\stop-all.ps1 jnpf-lims jnpf-dms 停止多个服务
|
.\stop-all.ps1 --help 显示帮助信息
|
|
也可双击 stop-all.bat,或在 cmd 中: stop-all.bat [服务名...]
|
#>
|
|
param(
|
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
|
[string[]]$ServiceNames = @()
|
)
|
|
Set-StrictMode -Version Latest
|
$ErrorActionPreference = 'Stop'
|
$ServiceNames = @($ServiceNames)
|
|
$Script:BaseDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
$Script:ServicePidDir = Join-Path $Script:BaseDir '.pids'
|
|
# Must stay in sync with start-all.ps1. The JAR name is used to avoid killing
|
# an unrelated process if an old PID has already been reused by Windows.
|
$Script:Services = @(
|
[PSCustomObject]@{ Name = 'jnpf-gateway'; Jar = 'jnpf-gateway-6.1.0-RELEASE.jar'; Port = 30000 }
|
[PSCustomObject]@{ Name = 'jnpf-platform'; Jar = 'jnpf-platform-6.1.0-RELEASE.jar'; Port = 30002 }
|
[PSCustomObject]@{ Name = 'jnpf-biz-common'; Jar = 'jnpf-biz-common-6.1.0-RELEASE.jar'; Port = 30015 }
|
[PSCustomObject]@{ Name = 'jnpf-eln'; Jar = 'jnpf-eln-6.1.0-RELEASE.jar'; Port = 30013 }
|
[PSCustomObject]@{ Name = 'jnpf-lims'; Jar = 'jnpf-lims-6.1.0-RELEASE.jar'; Port = 30019 }
|
[PSCustomObject]@{ Name = 'jnpf-dms'; Jar = 'jnpf-dms-6.1.0-RELEASE.jar'; Port = 30016 }
|
)
|
|
function Write-ColorLine {
|
param(
|
[string]$Text,
|
[ConsoleColor]$Color = [ConsoleColor]::Gray
|
)
|
$previous = [Console]::ForegroundColor
|
[Console]::ForegroundColor = $Color
|
Write-Host $Text
|
[Console]::ForegroundColor = $previous
|
}
|
|
function Write-Ok {
|
param([string]$Text)
|
Write-ColorLine $Text Green
|
}
|
function Write-Warn {
|
param([string]$Text)
|
Write-ColorLine $Text Yellow
|
}
|
function Write-Err {
|
param([string]$Text)
|
Write-ColorLine $Text Red
|
}
|
|
function Show-Help {
|
$scriptName = if ($PSCommandPath) { Split-Path -Leaf $PSCommandPath } else { 'stop-all.ps1' }
|
Write-Host ''
|
Write-Host 'JNPF 微服务批量停止脚本(Windows)'
|
Write-Host ''
|
Write-Host "用法: .\$scriptName [服务名...]"
|
Write-Host ''
|
Write-Host '省略服务名时停止全部服务;指定服务名时只停止对应服务:'
|
Write-Host ' jnpf-gateway jnpf-platform jnpf-biz-common'
|
Write-Host ' jnpf-eln jnpf-lims jnpf-dms'
|
Write-Host ''
|
Write-Host '示例:'
|
Write-Host " .\$scriptName"
|
Write-Host " .\$scriptName jnpf-lims"
|
Write-Host " .\$scriptName jnpf-lims jnpf-platform"
|
Write-Host ''
|
Write-Host '脚本只处理 start-all.ps1 写入的 .pids/*.pid,不会停止未登记的端口占用进程。'
|
}
|
|
function Resolve-TargetServices {
|
param([string[]]$Names)
|
|
if ($Names.Count -eq 0) {
|
return @($Script:Services)
|
}
|
|
$targets = @()
|
foreach ($name in $Names) {
|
$service = $Script:Services |
|
Where-Object { $_.Name -eq $name } |
|
Select-Object -First 1
|
if (-not $service) {
|
$available = ($Script:Services | ForEach-Object { $_.Name }) -join ' '
|
throw "未知服务名: $name`n可用服务:$available"
|
}
|
$targets += $service
|
}
|
return $targets
|
}
|
|
function Get-PidFilePath {
|
param([string]$Name)
|
return Join-Path $Script:ServicePidDir "$Name.pid"
|
}
|
|
function Get-RecordedPid {
|
param([string]$PidFile)
|
|
$raw = (Get-Content -LiteralPath $PidFile -Raw -ErrorAction SilentlyContinue)
|
$recordedPid = 0
|
if ($null -eq $raw -or -not [int]::TryParse($raw.Trim(), [ref]$recordedPid) -or $recordedPid -le 0) {
|
return $null
|
}
|
return $recordedPid
|
}
|
|
function Get-ProcessCommandLine {
|
param([int]$ProcessId)
|
|
$process = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = $ProcessId" -ErrorAction SilentlyContinue
|
if ($process) { return [string]$process.CommandLine }
|
return ''
|
}
|
|
function Test-RecordedProcessMatchesService {
|
param(
|
[int]$ProcessId,
|
[PSCustomObject]$Service
|
)
|
|
$commandLine = Get-ProcessCommandLine -ProcessId $ProcessId
|
if ([string]::IsNullOrWhiteSpace($commandLine)) { return $true }
|
|
$jarPattern = [regex]::Escape($Service.Jar)
|
$namePattern = [regex]::Escape($Service.Name)
|
return ($commandLine -match $jarPattern -or $commandLine -match $namePattern)
|
}
|
|
function Test-ProcessAlive {
|
param([int]$ProcessId)
|
if ($ProcessId -le 0) { return $false }
|
return [bool](Get-Process -Id $ProcessId -ErrorAction SilentlyContinue)
|
}
|
|
function Stop-ProcessTree {
|
param([int]$ProcessId)
|
|
$taskkill = Join-Path $env:SystemRoot 'System32\taskkill.exe'
|
if (Test-Path -LiteralPath $taskkill) {
|
$previousErrorAction = $ErrorActionPreference
|
$ErrorActionPreference = 'Continue'
|
& $taskkill /PID $ProcessId /T /F 2>$null | Out-Null
|
$ErrorActionPreference = $previousErrorAction
|
return ($LASTEXITCODE -eq 0)
|
}
|
|
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
|
return ($LASTEXITCODE -eq 0 -or -not (Test-ProcessAlive $ProcessId))
|
}
|
|
function Stop-JnpfService {
|
param([PSCustomObject]$Service)
|
|
$name = $Service.Name
|
$pidFile = Get-PidFilePath $name
|
|
if (-not (Test-Path -LiteralPath $pidFile)) {
|
Write-Warn " [跳过] $name — 无 PID 文件"
|
return $true
|
}
|
|
$processId = Get-RecordedPid -PidFile $pidFile
|
if ($null -eq $processId) {
|
Write-Warn " [清理] $name — PID 文件内容无效"
|
Remove-Item -LiteralPath $pidFile -Force -ErrorAction SilentlyContinue
|
return $true
|
}
|
|
if (-not (Test-ProcessAlive $processId)) {
|
Write-Warn " [跳过] $name — 进程已不存在 (PID $processId)"
|
Remove-Item -LiteralPath $pidFile -Force -ErrorAction SilentlyContinue
|
return $true
|
}
|
|
if (-not (Test-RecordedProcessMatchesService -ProcessId $processId -Service $Service)) {
|
Write-Warn " [跳过] $name — PID $processId 当前不是该服务,保留 PID 文件供人工检查"
|
return $false
|
}
|
|
if (Stop-ProcessTree -ProcessId $processId) {
|
Start-Sleep -Milliseconds 300
|
if (Test-ProcessAlive $processId) {
|
Write-Warn " [警告] $name — 已发送停止命令,但 PID $processId 仍在运行"
|
return $false
|
}
|
Write-Ok " [停止] $name (PID $processId)"
|
Remove-Item -LiteralPath $pidFile -Force -ErrorAction SilentlyContinue
|
return $true
|
}
|
|
Write-Err " [失败] $name — 无法停止 PID $processId"
|
return $false
|
}
|
|
if ($ServiceNames.Count -eq 1 -and $ServiceNames[0] -in @('help', '--help', '-h')) {
|
Show-Help
|
exit 0
|
}
|
|
$targets = @(Resolve-TargetServices -Names $ServiceNames)
|
Write-Host '========================================'
|
Write-Host " 停止 JNPF 微服务 (本次 $($targets.Count) 个)"
|
Write-Host '========================================'
|
|
$failed = 0
|
foreach ($service in $targets) {
|
if (-not (Stop-JnpfService -Service $service)) { $failed++ }
|
}
|
|
Write-Host '========================================'
|
if ($failed -gt 0) {
|
Write-Err "停止完成,但有 $failed 个服务需要人工检查。"
|
exit 1
|
}
|
Write-Ok '停止完成。'
|
exit 0
|