11 KiB
FT546 实时只读影子运行手册
1. 适用范围
本手册只用于运行独立的 ruoyi-wcs-shadow 进程,采集新调度算法的只读影子证据。
- 现场
LEGACYWCS 始终是唯一生产调度者、PLC 写入者和 WMS 回调者。 - 本阶段不切换
STANDARD,不允许 shadow 连接 PLC/WMS,也不允许写生产业务表。 - 停止或移除 shadow 进程就是本阶段的回滚;不得停止或切换稳定 WCS。
- 本地自动化测试不能替代 SQL Server、网络隔离或现场观察门禁。
2. 当前验证边界
| 门禁 | 当前状态 | 允许的结论 |
|---|---|---|
| Java 单元、Spring 上下文、H2 E2E | 可在本地执行 | 代码级只读链路和协议证据可验证 |
| PowerShell 脚本生命周期 | 可用无网络 sleeper jar 本地执行 | 确认、哈希、PID 精确启停和离线导出逻辑可验证 |
| SQL Server 集成 | NOT_RUN |
不得宣称数据库集成或现场就绪 |
| PLC/WMS 网络 deny | NOT_RUN |
必须由目标 shadow 主机的新鲜审计报告证明 |
| HIL 与现场影子窗口 | NOT_RUN |
不得部署或开始采集 |
application-shadow-local.yml 是外部配置模板,不是可直接运行的 H2 profile。H2 仍是 test-scope 依赖,且测试资源不会进入打包 jar;没有审批过的独立 SQL Server 环境时,不运行 jar 本地 smoke。
当前 shadow_hold_history 能跨重启恢复 wait edge 和连续 HOLD 计数,测试覆盖第三次相同 HOLD 后计数为 3。生产调度内核尚无消费该计数并触发 reroute 的策略,因此不得把“计数恢复”报告成“第三次 HOLD 已重规划”。该语义应在取得影子证据并明确路线/阻塞资源状态后单独设计。
3. 前置条件
开始任何现场操作前,逐项确认:
- 稳定 WCS 健康,实际制品、配置、PID、命令行和监听端口可读取。
- source 登录只能读取
shadow_ro.*批准视图,无生产表INSERT、UPDATE、DELETE、EXECUTE、ALTER、CONTROL或所有权权限。 - evidence 数据库与 source 数据库物理分离,evidence 登录只在 evidence 数据库具备所需写权限。
- shadow 部署目录、日志目录、端口和 PID 文件均与稳定 WCS 分离。
- shadow 主机到 PLC 写端点和生产 WMS 回调端点已在网络层拒绝,并生成不超过 60 分钟的 JSON 审计报告。
- 已约定生产 DB 延迟、shadow CPU/内存、source lag 和 evidence 磁盘阈值。
- 已准备立即停止 shadow 的值守人员;不安排任何稳定 WCS 切换动作。
权限报告最小结构:
{
"generatedAtUtc": "2026-08-14T10:00:00Z",
"status": "PASS",
"source": {
"server": "source-host",
"database": "FT546-wcs",
"login": "ft546_shadow_reader",
"approvedViewsReadable": true,
"productionWritePermissionCount": 0
},
"evidence": {
"server": "evidence-host",
"database": "FT546-shadow",
"login": "ft546_shadow_writer",
"requiredWritesGranted": true
}
}
网络 deny 报告最小结构:
{
"generatedAtUtc": "2026-08-14T10:00:00Z",
"status": "PASS",
"plcEgressDenied": true,
"wmsEgressDenied": true
}
报告必须来自已批准环境的实际检查。不得手工伪造 PASS。
4. 构建与本地验证
在隔离工作树执行:
mvn clean test
mvn -pl ruoyi-wcs-shadow -am -DskipTests package
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
-File .\tools\shadow\tests\Test-Ft546ShadowScripts.ps1
打包后确认 shadow jar 不含 H2 或测试 profile:
jar tf .\ruoyi-wcs-shadow\target\ruoyi-wcs-shadow-3.8.9.jar |
Select-String 'h2|application-shadow-test|application-shadow-local'
预期没有匹配项。该检查证明打包边界,不证明现场可运行。
5. 捕获稳定 WCS 基线
使用现场实际路径和 PID,不用 Git 分支或远端引用代替已部署制品:
.\tools\shadow\Capture-StableWcsManifest.ps1 `
-StableArtifact 'D:\wcs-stable\ruoyi-admin.jar' `
-StablePid 1234 `
-Configuration 'D:\wcs-stable\application.yml' `
-OutputJson 'D:\shadow-evidence\stable-manifest.json'
归档输出 JSON。确认其中的 artifact/config SHA-256、PID、启动时间、命令行和监听端口与现场一致。
6. 准备 shadow 目录和配置
将打包的 shadow jar 和一份外部 site 配置放在独立部署目录。配置只允许以下网络相关内容:
- loopback 状态端点;
- source SQL Server 只读连接;
- 独立 evidence SQL Server 写连接。
配置不得包含 PLC IP/port 或 WMS URL。密码使用受控环境变量或主机密钥设施,不写入 Git、预检 JSON、PID 文件或命令行。
Start-Ft546Shadow.ps1 会从成功预检向进程注入 artifact/config SHA-256、source server/database、loopback address/port 和磁盘阈值,供应用内 startup gate 复核。
7. 运行预检
.\tools\shadow\Test-Ft546ShadowPreflight.ps1 `
-StableManifest 'D:\shadow-evidence\stable-manifest.json' `
-ShadowArtifact 'D:\ft546-shadow\ruoyi-wcs-shadow.jar' `
-Config 'D:\ft546-shadow\application-shadow-site.yml' `
-DeploymentDirectory 'D:\ft546-shadow' `
-PermissionReport 'D:\shadow-evidence\permission-report.json' `
-NetworkDenyReport 'D:\shadow-evidence\network-deny-report.json' `
-StatusUri 'http://127.0.0.1:18091/shadow/status' `
-MinimumFreeBytes 10737418240 `
-OutputJson 'D:\shadow-evidence\shadow-preflight.json'
预检会失败关闭并留下 JSON,检查内容包括:
- 稳定 PID 健康且 artifact/config 哈希未变;
- shadow jar/config 位于独立部署目录;
- YAML 中 source/evidence server、database、username 与权限报告一致;
- source/evidence 数据库不同;
- 权限和网络 deny 报告为新鲜
PASS; - 状态端点为 loopback,端口不与稳定 WCS 冲突;
- 配置无 PLC/WMS 端点;
- evidence 磁盘空间达到阈值;
- shadow 部署目录中不存在当前
shadow.pid.json。即使其中记录的进程已退出,陈旧 PID 文件也会使预检失败;必须先核验并按停机/归档流程处理,禁止覆盖后启动。
任何一项失败都不得启动。
7.1 处理陈旧 PID 记录
不得删除或覆盖 shadow.pid.json。先读取其中的 pid:如果该 PID 仍存在,只能执行第 10 节的 Stop-Ft546Shadow.ps1,由脚本校验启动时间、Java 可执行文件、shadow jar 哈希和 -jar 命令行;校验不一致时停止操作并升级处理。
只有在 Get-Process 连续两次(间隔至少 5 秒)均确认该 PID 不存在后,才可由两名复核人员将原记录改名为 shadow.stale.<UTC timestamp>.json。变更单必须保留原记录 SHA-256、两次查询输出、归档路径、操作者和复核人。归档后重新执行完整预检,旧的预检 JSON 不得复用。
8. 启动与初始观察
只有经过双人复核的成功预检才能启动:
$started = .\tools\shadow\Start-Ft546Shadow.ps1 `
-ConfirmReadOnlyShadow `
-PreflightJson 'D:\shadow-evidence\shadow-preflight.json'
$started
Invoke-RestMethod 'http://127.0.0.1:18091/shadow/status'
启动后立即重新核对:
- 稳定 PID、制品哈希、健康状态和 PLC writer 所有权均未变化。
- shadow PID 与稳定 PID 不同;PID 文件位于 shadow 部署目录。
- shadow 只监听预检中的 loopback 端口。
- source 审计只有批准视图的
SELECT;生产业务表写审计为零。 - PLC/WMS 网络监控没有 shadow 连接尝试。
先以低频轮询观察 15 分钟。只有稳定 WCS 指标不变、source 查询延迟在阈值内、source lag 正常、evidence 持续增长且磁盘安全时,才可延长观察窗口。
STARTING 表示尚无完整 cycle;READY 表示最近 cycle 完成;DEGRADED 必须按 reason code 调查。证据存储失败或碰撞会永久停止该进程继续读取,需停止并保留现场。
9. 立即停止条件
出现任一条件,立即停止 shadow,不等待当前观察窗口结束:
- 稳定 WCS 健康、CPU、内存、线程、PID 或 PLC writer 所有权变化;
- 生产 DB 延迟超过约定阈值,或出现锁等待/连接池压力;
- source 权限、数据库身份或批准视图发生变化;
- source lag 超出事实新鲜度,或连续 query timeout;
- 任意 PLC/WMS 连接尝试或任意生产表写审计;
- evidence 磁盘低于阈值、写失败、哈希碰撞或 evidence backlog 异常;
- shadow CPU/内存超过约定上限;
- 状态为
DEGRADED且原因不能立即解释为短暂 source 故障。
10. 精确停止与回滚
.\tools\shadow\Stop-Ft546Shadow.ps1 `
-PidFile 'D:\ft546-shadow\shadow.pid.json'
停止脚本只接受该 PID 文件记录的进程,并逐项验证 PID、启动时间、Java 可执行文件、shadow jar 哈希和 -jar 命令行。任何不一致都拒绝停止,不会按 java 进程名批量查杀。成功后 PID 记录改名归档为 shadow.stopped.<timestamp>.json。
回滚完成条件:shadow PID 不存在,稳定 PID/健康/PLC 所有权保持原值,网络与数据库审计无 shadow 副作用。稳定 WCS 从未停止或切换。
11. 导出证据
现场 SQL Server 模式使用集成认证,或把 SQL 密码短暂放入当前进程环境变量 FT546_SHADOW_EVIDENCE_PASSWORD:
.\tools\shadow\Export-Ft546ShadowEvidence.ps1 `
-EvidenceServer 'evidence-host' `
-EvidenceDatabase 'FT546-shadow' `
-EvidenceUsername 'ft546_shadow_writer' `
-RunId '00000000-0000-0000-0000-000000000000' `
-OutputDirectory 'D:\shadow-evidence\export-20260814'
离线复核模式接受已审批工具生成的快照目录:
.\tools\shadow\Export-Ft546ShadowEvidence.ps1 `
-SnapshotDirectory 'D:\shadow-evidence\approved-snapshot' `
-RunId '00000000-0000-0000-0000-000000000000' `
-OutputDirectory 'D:\shadow-evidence\export-20260814'
输出固定包含:
cycles.csv、cycles.json;protocol-differences.csv;algorithm-differences.csv;insufficient-facts.csv;startup-checks.json;manifest.json、manifest.sha256。
校验:
$expected = (Get-Content 'D:\shadow-evidence\export-20260814\manifest.sha256').Split()[0]
$actual = (Get-FileHash 'D:\shadow-evidence\export-20260814\manifest.json' -Algorithm SHA256).Hash.ToLowerInvariant()
if ($expected -ne $actual) { throw 'EVIDENCE_MANIFEST_HASH_MISMATCH' }
将稳定 manifest、preflight JSON、权限报告、网络 deny 报告、启动/停止 PID 记录、状态响应、现场监控截图和导出目录作为同一批次归档。
12. 禁止事项
- 不在生产数据库内创建 evidence schema 代替独立 evidence 数据库。
- 不给 source 登录任何生产写权限。
- 不把
ruoyi-admin以SHADOW模式启动。 - 不允许 shadow 配置 PLC/WMS endpoint。
- 不用宽泛 Java 进程查杀命令代替
Stop-Ft546Shadow.ps1。 - 不因为本地/H2 测试通过就跳过 SQL Server、网络或现场门禁。
- 不把本阶段结果解释为
STANDARD上线许可。