“成功了,请拿出证据”——一次屏幕插播的四级取证,与 ROMA 查不到日志的正解

现场在发布管理页面提交了一次文字插播,随后抛来两个问题:这次到底成没成功?为什么在 ROMA 平台的设备命令日志里搜不到任何记录?第一个问题要证明”发生过”,第二个问题要证明”本来就不该有”。这次排查的价值不在结论本身,而在于用代码、配置、进程、日志四级互相独立的证据,把一次成功钉死,同时给”查不到日志”一个经得起追问的解释。

先回答:成功了,而且证据链闭环

本次插播已成功提交到大华 MPS 平台:下游 HTTP 状态 200,业务响应 success=trueretCode=0,返回播放任务 ID playId=202608241152272881,目标设备为电梯厅 1 号屏。但有一个必须声明的边界:证据能确认”平台已受理并生成播放任务”,不能单独证明物理屏幕现场已经亮出来——现场显示要靠屏幕观察或设备状态确认。把成功边界说清楚,比一句笼统的”成功了”有用得多。

成功边界:受理≠显示
图 1:证据链止步于”平台受理”。从受理到物理显示这最后一程,超出本次证据的覆盖范围。

完整链路:四层调用,ROMA 不在其中

1
2
3
4
5
6
7
8
flowchart LR
A["发布管理页面<br/>releaseMng"] -->|POST /publishInfo/play_inter| B["smart-service<br/>保存节目(事务内)<br/>factory=2 大华分支"]
B -->|Feign submitPlayNow| C["integrate-iot"]
C -->|POST /dh/savePlayInfo| D["display_app.jar<br/>:9120"]
D -->|POST /mps/playPlan/submitPlayInfo| E["大华 MPS<br/>success=true retCode=0"]
E -->|playId| B
B --> F["写入 screen_publish.play_id<br/>事务提交"]
style E fill:#ffd6a0

链路里几个值得记住的细节:

  • 节目名在 smart 侧会追加随机数demo-text(7916325)),防止下游重名;
  • 文字节目的文字内容直接写入 materialIds;没有 beginDate 时走立即插播 submitPlayNow
  • 下发结果为空会抛”发布失败”并回滚事务——节目记录和下发结果是同一个事务,不会出现”库里写着发布成功但屏没收到”的孤儿数据;
  • 代码层的成功判定写在 DhScreenResp.success() 里:returnCode == 200 || retCode == 0,二者居其一即为成功,playId 非空是任务已生成的进一步证据。

四级证据,一级都不能少

四级证据:代码、配置、进程、日志
图 2:四级证据互相独立、互相印证——任何一级对不上,结论都不能成立。

① 代码证据:从前端 addpublishInfoScreenPublishServiceImpl,再到 PublishInfoProcessor 的大华分支和 DhScreenClientImpl 的 RestTemplate 调用,逐段确认真实调用顺序。重点核实的是 Feign 定义:submitPlayNow 走服务间调用(目标服务 iot),不经过外部网关。

② Nacos 配置证据:线上导出的 integrate-iot.yml 里,有效配置是:

1
2
3
4
5
gateway:
screen:
dh:
host: http://10.152.64.7:9120
rest-log-enable: true

导出配置里**还躺着一段 platform.things.dh-screen**,但当前代码的 PlfThingsProperties 根本没有 dhScreen 字段,仓库里也搜不到任何调用点——它是一段历史遗留的僵尸配置,不参与链路。这一步很关键:如果只看”配置里有什么”而不看”代码用了什么”,就会错误地把 ROMA 算进链路里。配置导出日期是 6 月,而 8 月 24 日的线上日志显示请求目标与导出值一致,运行时与配置快照互相印证。

③ 进程证据:在 10.152.64.7 上执行 ss -lntp | grep ':9120',确认 9120 端口是 display_app.jar(启动参数 -Dloader.path=/mnt/display-app/lib/,工作目录 /mnt/display-app),不是 smart-service 也不是 integrate-iot。链条上每一环的”物理身份”都要核实,不能靠猜。

④ 日志证据:三段日志的时间戳严丝合缝——

1
2
3
4
11:53:17 integrate-iot  submitPlayNow req=programName=demo-text(7916325), deviceIds=[2]
11:53:31 display_app dh/savePlayInfo 收到数据
11:53:32 display_app POST https://192.168.0.223:8443/mps/playPlan/submitPlayInfo
11:53:32 MPS 响应 Status=200, success=true, retCode=0, playId=202608241152272881

请求从 integrate-iot 发出 → display_app 入站 → display_app 调大华 MPS → MPS 返回成功,每一跳都有日志,首尾时间差在秒级以内。

ROMA 为什么查不到日志:先证明”该走哪条路”

答案现在很直白:本次请求走的是 gateway.screen.dh → display_app → 大华 MPS根本没有进入 platform.things → ROMA 设备命令 → MQS 响应 那条通道,所以在 ROMA 的设备命令日志里搜节目名、playId 或接口路径,注定一无所获。

两条路径:实际走的与没走的
图 3:实际链路经过 display_app 直达 MPS;ROMA 通道存在于配置里,却没有代码调用它。

这里有个方法论值得单独说:**”查不到日志”有两种完全相反的含义——没发生,或者发生在别处**。下结论之前,必须先从代码和配置出发证明”这次调用应该走哪条路”,再谈”那条路上有没有日志”。如果顺序反过来,拿着”ROMA 没日志”去推”插播没成功”,就制造了一起冤案。至于大华 MPS 内部是否还有更深一层的平台间调用,那超出本项目可见边界,需要 MPS 的负责人提供其内部链路——把”我不知道的边界”也写进报告,是取证的一部分。

可直接复用的复查命令

1
2
3
4
5
6
7
8
9
# IoT 日志按节目名查最近一次插播
grep -a -n -E 'savePlayInfo|submitPlayNow' iot-all.log | tail -20

# display_app 精简查看请求与响应
grep -a -E 'HTTP请求 - URL: .*submitPlayInfo|HTTP响应 - URL: .*submitPlayInfo' \
log_display_app.log | tail -3 | cut -c1-500

# 确认 9120 端口的真实身份
ss -lntp | grep ':9120'

收尾

这次排查最终交付的是一份可以直接回复上游的结论:插播已到达大华 MPS 并被受理(附 playId 与设备),现有链路不经 ROMA 属设计如此,ROMA 无日志是正常现象;平台受理与物理显示是两件事,后者需现场确认。四级证据里任何一级单独看都有替代解释——日志可能不全、配置可能过期、代码可能有别的分支——但四级独立证据指向同一个结论时,它就是可靠的。排障报告的说服力,从来不来自语气,而来自证据之间的互相印证。