写在前面#
这篇是上一篇 政务系统可观测平台搭建实战:SigNoz + OpenTelemetry 探针 的续集。
上一篇写的是"把平台和探针装起来"——SigNoz、ClickHouse、OTel Collector,给 5 台服务器 9 个应用挂探针。那一篇写完的时候,平台能跑、图表能出,我以为活儿干完了。
事实证明只干了一半。
平台"能跑",和平台"能说清数据、能看借口",完全是两件事。 这次的任务是把一套已经装好的可观测平台,从"装完什么样就是什么样"变成"乌兰察布这套系统该有的样子"。中间动的东西比我想的多:
- 前端页面里全是历史环境的地址——IP、仪表板 UUID、工单接口、SDK 上报地址,一个都不能直接用
- CMDB 里 1000 多条全是示例数据,主机名叫
SRV-202401001,接口路径里带guizhou,一眼就知道不是我们这套 - 给应用挂探针,重启一个系统直接起不来,日志只有一行提示,什么都没说
- 接口监控页面录了 138 个接口,调用次数全是 0
- 最后查出来的原因是:可观测平台那台服务器的系统时钟慢了 29 分钟
这些坑有个共同点:报错信息不会告诉你在哪。 只能一层一层往下挖。
排查过程中我大量用了 AI 帮忙:反编译 class 文件看逻辑、写批量查 ClickHouse 的脚本、比对两份配置的差异、生成验证命令。说句实话,没有 AI 我大概要多花三四倍时间,特别是反编译和写查询脚本这两块,它比我手快得多。
但 AI 也有明显的短板:它不知道现场。 有一次它判断某个应用起不来是"外网授权服务不可达",让我去查网络。我回了一句"网络一点没动,就是重启了一下",它才转向去查 jar 包,最后在反编译出来的字节码里找到了一个硬编码的日期。类似的情况还有几次,所以每个结论我都得自己再验一遍。
这篇会写:
- 20 个问题的现象、怎么查出来的、最后怎么解决的
- 改了哪些文件、每处改动到底是干什么的
- 踩过的坑,以及下次怎么少踩
脱敏说明:本文所有 IP、账号、密码、域名都换成了占位符(
APP-HOST、SIG-HOST、<密码>这类),路径和参数保留原样。换成你自己的环境就能用。
环境说明#
先交代一下本文用到的代号,后面对应的都是这些:
| 代号 | 角色 | 说明 |
|---|---|---|
APP-HOST | 应用服务器 | Nginx、网关、两个微服务、4 个 Tomcat 都在这台 |
APP-HOST-B | 上面的 B 区网卡地址 | 双网卡主机,A/B 区各一个地址 |
DB-HOST | 数据库服务器 | MySQL 5.7(平台库)+ Oracle(业务库) |
DB-HOST-B | 上面的 B 区网卡地址 | 同上 |
SIG-HOST | 可观测平台服务器 | SigNoz、ClickHouse、OTel Collector |
SIG-HOST-B | 上面的 B 区网卡地址 | 同上 |
BIZ-HOST-1/2/3 | 其他业务服务器 | 云表单、链条审批、审管互动 |
OLD-HOST-* | 平台初始配置里的历史地址 | 全部需要替换 |
EXT-HOST-* | 外部环境的地址 | 与本环境不通 |
为什么要强调 A 区 / B 区:这套环境是双网卡,A 区给用户访问,B 区给服务器之间互通。前端(浏览器)必须用 A 区地址,后端(服务器)必须用 B 区地址,填反了页面就空,或者数据上报不进来。这个坑后面会专门说。
部署形态:
| 组件 | 说明 |
|---|---|
| 4 个微服务 | 资源服务(8087)、业务服务(8089)、网关(30901)、BSPPLUS(9099) |
| Nginx | 80 端口,统一入口:/obs/ 前端、/gate/ 网关、/ucenter/ 登录 |
| 前端 | 打包产物解压到 Nginx 的 html 目录,访问路径 /obs |
| SigNoz | 8086 前端、8123 ClickHouse HTTP、4317/4318 OTLP 上报 |
| Tomcat | 4 个实例,分别承载通用审批、并联审批、综合受理、网办后台 |
第一部分 部署过程中遇到的问题与解决#
1.1 问题总览#
| # | 问题 | 影响面 | 状态 |
|---|---|---|---|
| 1 | 网关全部接口返回「缺少Authorization」 | 所有业务接口 401 | 已解决 |
| 2 | 服务启动即退出(license.dat 缺失) | BSPPLUS / 网关起不来 | 已解决 |
| 3 | .dbmd 建库脚本报排序规则不支持 | 数据库无法初始化 | 已解决 |
| 4 | 数据迁移时字段超长 / 权限不足 / 无法跳过建表 | 147 张表数据迁移 | 已解决 |
| 5 | bat 脚本乱码、吞换行 | 服务无法启动 | 已解决 |
| 6 | 执行 startup.bat 后命令行永久挂住 | 运维操作受阻 | 已解决 |
| 7 | 加探针后 Tomcat 报「‘xxx’ 不是内部或外部命令」 | 服务启动失败 | 已解决 |
| 8 | 网办后台(iWebSite)启动 10 秒后自动退出 | 生产系统不可用 | 已解决 |
| 9 | 换最新 license.dat 仍无法启动 | 排查方向错误 | 已纠正 |
| 10 | 告警「查看工单」点击后长时间无响应 | 功能不可用 | 已解决 |
| 11 | 修改前端配置后浏览器仍用旧值 | 配置不生效 | 已解决 |
| 12 | 接口监控页面完全空白 | 功能不可用 | 已解决 |
| 13 | 接口监控「调用总次数」始终为 0 | 功能不可用 | 已解决 |
| 14 | 接口调用总览 P50/P95/P99 面板报错 | 面板不可用 | 已解决 |
| 15 | 告警详情都是公司的实例地址 | 原为不可达地址 | 已解决 |
| 16 | CMDB 初始数据需替换为公司实际环境 | 数据失真 | 已解决 |
| 17 | 接口表唯一键冲突 | 数据无法入库 | 已解决 |
| 18 | nacos\bin\logs 占用 17.4 GB | 磁盘浪费 | 已清理 |
| 19 | SigNoz 服务器时钟偏差 29 分钟 | 所有时间窗查询偏移 | 已解决 |
| 20 | 性能问题(部署期间顺带发现) | 待优化 | 已记录 |
1.2 问题详述#
问题 1:网关全部接口返回「缺少Authorization」#
现象
GET /gate/bspplus/user/login → {"code":0,"msg":"缺少Authorization","status":400}
所有经过网关的请求都被拦下,包括本该免登录的登录接口。
根因
网关的鉴权链路由两个过滤器组成:
GlobalApiAuthFilter:负责anon-white-list(匿名白名单)和session.loginurisGlobalAuthorizationFilter:负责tokenWhiteList(Token 白名单)
网关配置里 api-auth-check-enable: false 关闭了 GlobalApiAuthFilter,导致 anon-white-list 里的配置完全不生效。最终只有 GlobalAuthorizationFilter 在跑,而它只认 tokenWhiteList。
判断逻辑(反编译确认):
private boolean isWhite(String path) {
return securityCustomConfig.getTokenWhiteList().stream()
.anyMatch(p -> pathMatcher.match(p, path));
}
处理
在 Nacos 的 dgov-cloud-gateway 配置里,往 tokenWhiteList 追加 6 条:
tokenWhiteList:
- /gate/bspplus/utils/**
- /gate/bspplus/user/login
- /gate/bspplus/user/signOn/login
- /gate/bspplus/oauth/**
- /gate/bspplus/sso/**
- /gate/bspplus/user/logout
验证
GET /gate/bspplus/utils/uuid → HTTP 200,返回 uuid
教训
网关有两套白名单机制,只改其中一套没用。排查时应当先确认哪个过滤器在生效(看 api-auth-check-enable 开关),再决定改哪个配置项。
问题 2:服务启动即退出(license.dat 缺失)#
现象
java.io.FileNotFoundException: class path resource [license.dat] cannot be opened
BSPPLUS 和网关日志打印 Started 之后立刻退出。
根因
SystemApp.checkLicence() 在启动时校验 License:
goto("utf-8") → ClassPathResource / FileUrlResource("license.dat")
→ Base64 + DES 解密 → 校验 JSON 里 code == "1"
license.dat 文件名在 com.inspur.dgov.core.a.c.class() 中硬编码(ldc #20 'license.dat')。
处理
从已经正常运行的通用审批(XZSP)目录复用 license.dat:
D:\server\apache-tomcat-9.0.87-XZSP\...\license.dat
→ D:\server\inspur-digital-bspplus\license.dat
→ D:\server\dgov-cloud-gateway\license.dat
该 License 有效期至 2031-03-03。
教训
同一产品线的 License 通常可以复用。建议把可用的 license.dat 统一留存一份到 D:\server\_license\,后续服务重装直接拷贝。
问题 3:.dbmd 建库脚本报排序规则不支持#
现象
导入 .dbmd 时提示 Unknown collation: 'utf8mb4_0900_ai_ci'。
根因
utf8mb4_0900_ai_ci 是 MySQL 8.0 引入的排序规则,本环境是 MySQL 5.7.17,不支持。
处理
替换脚本内所有 utf8mb4_0900_ai_ci → utf8mb4_general_ci,重建后 147 张表创建成功。
问题 4:数据迁移的三个问题#
4.1 字段超长
ERROR 1406: Data too long for column 'USER_CODE'
表定义 VARCHAR(36),源库实际数据 56 字符。
解决:ALTER TABLE PUB_USER_ROLE MODIFY USER_CODE VARCHAR(100);
4.2 权限不足
ERROR 1044 (42000): Access denied for user 'dgov_bsp'@'%' to database 'dgov_bsp_tmp'
解决:用 root 执行 GRANT ALL PRIVILEGES ON dgov_bsp_tmp.* TO 'dgov_bsp'@'%';
4.3 数据传输工具无法跳过建表
目标库已有表结构,工具仍强制建表导致冲突。
解决:建临时库 dgov_bsp_tmp 先导入,再用 SQL 从临时库搬到正式库:
INSERT IGNORE INTO dgov_bsp.PUB_ROLE SELECT * FROM dgov_bsp_tmp.PUB_ROLE;
迁移结果
| 表 | 源库行数 | 目标库行数 |
|---|---|---|
| PUB_ROLE | 75 | 71 |
| PUB_RESOURCE | 2708 | 2541 |
| PUB_ROLE_RESOURCE | 4318 | 1511 |
| PUB_USER_ROLE | 125814 | 125814(INSERT IGNORE) |
问题 5:bat 脚本乱码、吞换行#
现象
执行 startup.bat 时输出:
'+' 不是内部或外部命令,也不是可运行的程序
'笈低?' 不是内部或外部命令
'MX' 不是内部或外部命令
'上报地址:' 不是内部或外部命令
根因(两个叠加)
- 文件用
LF换行写出,而cmd.exe要求CRLF - 文件内含 GBK 中文注释(
rem 应用: xxx),cmd 逐行解析时把rem和中文拆开,中文碎片被当作命令执行
处理
.bat 文件统一规范:
$lines = @('@echo off', 'set "JAVA_OPTS=..."', ...)
$text = ($lines -join "`r`n") + "`r`n" # 强制 CRLF
[System.IO.File]::WriteAllText($f, $text, (New-Object System.Text.ASCIIEncoding)) # 纯 ASCII
规范(后续所有 bat 都遵守)
- 换行必须
CRLF - 内容必须纯 ASCII,注释也用英文
- 不使用 heredoc 直接写文件(容易带入 LF)
问题 6:执行 startup.bat 后命令行永久挂住#
现象
& cmd /c "startup.bat" 2>&1 | Out-String | Write-Host # ← 卡死,不再返回
根因
startup.bat 内部调用 catalina.bat start,会 start 一个新窗口运行 java。Java 进程继承了父进程的 stdout 管道句柄,只要 java 活着,管道就不关闭,PowerShell 就一直等待。
处理
改用 Start-Process,不接管输出:
Start-Process -FilePath "cmd.exe" `
-ArgumentList "/c","startup.bat" `
-WorkingDirectory "$base\bin" `
-WindowStyle Hidden
教训
凡是会驻留后台的进程(Tomcat、java、nginx),都不要用管道接收它的输出,否则管道永不关闭。
问题 7:加探针后 Tomcat 报「‘xxx’ 不是内部或外部命令」#
现象(同问题 5,但发生在探针配置里)
'+' 不是内部或外部命令
'MX' 不是内部或外部命令
'上报地址:' 不是内部或外部命令
根因
写 setenv.bat 时用了 GBK 编码 + 中文注释 + LF 换行。
处理
重写为纯 ASCII + CRLF,注释全改英文。三个文件校验结果:
ACCEPT: 字节=858 非ASCII=0 CRLF=11 LF-only=0 [通过]
Parallel: 字节=858 非ASCII=0 CRLF=11 LF-only=0 [通过]
WebSite: 字节=864 非ASCII=0 CRLF=11 LF-only=0 [通过]
问题 8:网办后台(iWebSite)启动 10 秒后自动退出#
现象
Tomcat 正常启动,Spring 容器初始化完成,然后静默退出,无异常栈:
14:41:12 Root WebApplicationContext: initialization completed in 9651 ms
14:41:12 com.inspur.app.utils.DateUtils.compareDate(DateUtils.java:43)
- Application is not initialize...
↓ 等 10 秒
14:41:22 Closing Root WebApplicationContext
14:41:22 {dataSource-2} closed / {dataSource-1} closed
排查过程
- 首先怀疑外网授权服务
EXT-HOST-4:8081不可达 → 测了一下确实不通。但我回了一句"网络一点没动,就是重启了一下",才意识到方向找错了 - 用
javap反编译jcore-1.0.jar里的com.inspur.app.utils.DateUtils
反编译结果
private static final String expireDate = "2025-10-01 00:00:00"; // ← 到期日硬编码
public static final String RUN_MODE = "release"; // ← 本方法未使用,无旁路
public static JSONObject compareDate() {
c1.setTime(df.parse("2025-10-01 00:00:00")); // 到期日
c2.setTime(new Date()); // 当前时间
int r = c1.compareTo(c2);
if (r == 0) { code="1"; log("Application is initialize..."); }
else if (r < 0) { // ★ 已过期
code = "0";
log("Application is not initialize...");
Thread.sleep(10000);
System.exit(0); // ★★★ 主动结束 JVM
}
else { code="1"; log("Application is initialize..."); }
}
根因
jcore-1.0.jar 把到期日写死成 2025-10-01,当前日期(2026-10-27)超过后 System.exit(0)。
为什么之前能跑:旧进程是在 2025-10-01 之前启动的,运行时不重查。本次为接探针重启,才触发检查。
对比扫描:其他产品都是新版 jar
| Tomcat | webapp | jar | 到期日 | 状态 |
|---|---|---|---|---|
| ACCEPT | Inspur.Dzzw.CloudAccept | jcore-3.5.jar | 2118-10-01 | 正常 |
| Parallel | Inspur.Dzzw.WebParallel | jcore-3.5.jar | 2118-10-01 | 正常 |
| XZSP | Inspur.Dzzw.WebApproval | jcore-3.5.jar | 2118-10-01 | 正常 |
| WebSite | iWebSite | jcore-1.0.jar | 2025-10-01 | 已过期 |
处理
用公司已发布的 jcore-3.5.jar 替换:
Copy-Item "...\XZSP\...\lib\jcore-3.5.jar" $lib\jcore-3.5.jar
Move-Item $lib\jcore-1.0.jar $lib\jcore-1.0.jar.disabled-<时间戳>
结果
8030 LISTENING PID 10252 ← 网办后台恢复
9996 LISTENING PID 10252 ← JMX 探针同时生效
教训(重要)
jcore-1.x→jcore-3.5跨大版本替换兼容(API 无破坏性变更),已验证- 每个老应用重启前都要确认 jar 版本与到期日,否则一重启就炸
- 我把这个结论反馈给了公司,让他们排查其他服务器上是否有同样的漏升级
问题 9:换最新 license.dat 仍无法启动#
现象
更换最新的 license.dat 后,重启仍然 10 秒退出。
我的判断
DateUtils.compareDate() 根本不读 license.dat:
expireDate是static final编译期常量,已内联进字节码- 方法内唯一用到 License 的地方是
com.iop.util.License.getLicenseName(),只取一个名字打日志
所以换 license.dat 对这个问题完全无效。
记录这一点的意义:后续再遇到"启动即退出"且日志有 DateUtils.compareDate,直接查 jar 版本,不要再从 license 或网络方向排查。
问题 10:告警「查看工单」点击后长时间无响应#
现象
点击告警的「查看工单」:
- 先等约 30 秒
- 浏览器 DevTools 报
Failed to load response data - 页面无任何提示
根因
前端 dmsApi.js 直连公司外网工单系统:
DMS_API_ROOT: "https://dms.example.com"
DMS_ORDER_INFO_PATH: "/local/dsm-plugin-oms/api/order/getInfo"
DMS_HTTP_TIMEOUT: 30000 // 30 秒
dms.example.com 在本环境不可达 → 请求挂 30 秒后才超时。
关键:前端逻辑分支
const o = await post(DMS_API_ROOT + DMS_ORDER_INFO_PATH, { busiId });
if (o.state !== 1) {
if (o.message.includes("TkOrder not found")) {
err.code = "TK_ORDER_NOT_FOUND"; // → 前端弹「尚未生成工单,是否创建?」
}
reject(...);
}
解决(不改前端代码,用 Nginx 造响应)
在 nginx.conf 加两条 location:
# ① 模拟「未找到工单」,让前端走友好分支
location = /dms/local/dsm-plugin-oms/api/order/getInfo {
default_type 'application/json; charset=utf-8';
add_header Cache-Control 'no-store';
return 200 '{"state":0,"message":"TkOrder not found: DMS not connected"}';
}
# ② 工单占位页
location ^~ /dms/ {
alias D:/server/nginx/html/dms/;
index index.html;
try_files $uri $uri/ /dms/index.html;
}
前端配置改为同源相对路径:
DMS_API_ROOT: '/dms',
DMS_DETAIL_PAGE_URL: '/dms',
DMS_CREATE_PAGE_URL: '/dms',
DMS_HTTP_TIMEOUT: 5000, // 30s → 5s
结果
| 阶段 | 行为 | 耗时 |
|---|---|---|
| 修复前 | 直接跳转外网 API 地址 / 等 30 秒报错 | 30 秒 |
| 修复后 | 5 秒内弹「是否创建工单」→ 点创建打开占位页 | ≤5 秒 |
问题 11:修改前端配置后浏览器仍用旧值#
现象
config.dms.js 已改成 DMS_HTTP_TIMEOUT: 5000,但浏览器仍按 30 秒超时。
根因
index.html 给配置文件带了固定版本号:
<script src="./config.dms.js?id=1792120301403"></script>
浏览器认为 URL 没变,一直用缓存。
处理
- 换版本号:
?id=1792120301403→?id=202610271345 - Nginx 对
config.*.js禁缓存:
location ~ ^/obs/config\.[a-z0-9]+\.js$ {
root D:/server/nginx/html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
expires -1;
}
教训
前端配置类文件(config*.js)必须禁缓存,否则改配置永远要手工强刷。
问题 12:接口监控页面完全空白#
现象
「接口调用详情」表格一行都没有。
根因(两个独立问题叠加)
13.1 默认筛选是「高危」,而导入的接口全是「非高危」
const D = 1; // 默认值 = 1(高危)
const g = r(D);
const P = async () => {
const a = await pe({ isHighRisk: g.value }); // ← 只查 is_high_risk=1
m.value = Array.isArray(a) ? a : (a?.data) || [];
};
13.2 导入的接口路径是「span 名」,不是「HTTP 路由」
之前从 ClickHouse 导入的 709 条是 span 的 name,包含 SQL / Dubbo 调用:
SELECT orcl.APPROVE_EXPORTALREQ ← SQL span
UPDATE orcl.ENTERPRISE_RESOURCE_SHARE ← SQL span
com.inspur.service.PowerManagerService/getItemInfoByItemCode ← RPC span
而接口监控只统计 HTTP 路由,后端 SQL 按 http.route / http.target 匹配,SQL 名永远匹配不上。
处理
重建 cmdb_app_interface,只保留真实 HTTP 路由:
| 应用 | 接口数 |
|---|---|
| 浪潮政务服务通用审批平台(XZSP) | 60 |
| 链条审批(QYSL) | 55 |
| 乌兰察布综合受理平台(ZHSL) | 8 |
| 电子政务公共服务平台 BSP | 7 |
| 浪潮政务云盘(DISK) | 2 |
| 网办后台(WEBSITE) | 2 |
| 并联审批系统(BLSP) | 2 |
| 浪潮电子政务任务调度系统 | 1 |
| 浪潮行政权力事项管理系统 | 1 |
| 合计 | 138 |
高危接口标注规则:平均耗时 ≥ 500ms 或 调用量 ≥ 90 次 → 共 19 条。
建表时遇到的问题:cmdb_app_interface 没有 app_name 列,首次插入 138 条全部报 ERROR 1054 Unknown column。实际列为:
id, app_id, service_id, api_name, api_path, method,
type, remark, create_time, update_time, create_by, update_by,
del_flag, is_high_risk
按实际列名重插后成功。
问题 13:接口监控「调用总次数」始终为 0#
现象
138 行接口全部显示 调用总次数 = 0、接口状态 = 无流量。
根因
前端 signozService 里的 SQL 用 http.target 做精确匹配:
AND attributes_string['http.target'] IN $http.route
GROUP BY attributes_string['http.route'] AS `接口地址`
而实际数据:
| 项 | 值 |
|---|---|
前端传的 api_path | /Inspur.Dzzw.WebApproval/approval/workextend |
ClickHouse http.target | /Inspur.Dzzw.WebApproval/approval/workextend?method=queryCount ← 带查询参数 |
ClickHouse http.route | /Inspur.Dzzw.WebApproval/approval/workextend ← 不带参数 |
IN 是精确匹配,http.target 带 ?method=queryCount → 匹配失败。
验证(直连 ClickHouse)
-- 用 http.target 精确匹配
SELECT ... WHERE attributes_string['http.target'] IN ('/Inspur.Dzzw.WebApproval/approval/workextend')
-- → 0 条
-- 用 http.route 精确匹配
SELECT ... WHERE attributes_string['http.route'] IN ('/Inspur.Dzzw.WebApproval/approval/workextend')
-- → 764 条
解决(前端补丁)
signozService-Cg3RwBMu.js:
-- 改前
AND attributes_string['http.target'] IN $http.route
-- 改后
AND attributes_string['http.route'] IN $http.route
第二层:网关 SQL 注入过滤器拦截
改为 http.route 后,接口返回:
{"code":0,"msg":"非法参数","status":400}
响应使用 msg 字段(网关格式),业务服务使用 message 字段,据此定位到网关。查网关配置:
security:
sql-injection-regex: ...|UNION.+?SELECT.+?FROM|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|...
查询体里含 SELECT ... FROM signoz_traces...,命中 (SELECT|DELETE).+?FROM。
网关 Nacos 配置追加参数白名单:
param-white-list[1]: /gate/obs/business/signoz/proxy/**
重启网关后,请求通过参数校验,卡在鉴权层(返回「缺少Authorization」),说明 SQL 过滤器已放行。
第三层:SigNoz 服务器时钟偏差
放行后数字能出来,但调 60 次只显示 3 次。经逐查询对比,定位为 SigNoz 服务器系统时钟慢 29 分钟,导致 BETWEEN ... AND now() 的时间窗上界偏移。详见「问题 19」。
最终处理
| 层 | 处理 |
|---|---|
| 第一层 | signozService-Cg3RwBMu.js 中 http.target 改为 http.route |
| 第二层 | 网关 Nacos 配置追加 param-white-list[1],重启网关 |
| 第三层 | SigNoz 服务器执行 hwclock --hctosys |
验证
校时后,同一接口 timestamp > now()-30m 与 BETWEEN now()-30m AND now() 结果一致(14 = 14);Postman 调 10 次后刷新页面,调用总次数随之增长。
问题 14:接口调用总览 P50/P95/P99 面板报错#
现象
总览页 P50/P95/P99 延迟面板显示红色报错图标,Console 输出:
error: Executable file ./histogramQuantile is not executable
inside user scripts folder /var/lib/clickhouse/user_scripts/
while executing 'FUNCTION histogramQuantile(...)'
根因
SigNoz 的 ClickHouse 镜像里,histogramQuantile UDF 脚本没有执行权限:
-rw-r--r-- 1 root root 1446040 Jun 29 01:29 histogramQuantile
↑↑↑ 没有 x
解决(在 SigNoz 服务器 SIG-HOST 上)
# 注意:docker exec 不做 shell 通配符展开,要用完整文件名
docker exec -u root signoz-clickhouse chmod +x /var/lib/clickhouse/user_scripts/histogramQuantile
# 确认
docker exec -u root signoz-clickhouse ls -l /var/lib/clickhouse/user_scripts/
# -rwxr-xr-x ... histogramQuantile
# 验证
docker exec signoz-clickhouse clickhouse-client --query \
"SELECT histogramQuantile([1.0,2.0,5.0,10.0], [10.0,20.0,30.0,40.0], 0.99) AS p99"
# 返回 nan(能执行即成功,nan 是因为测试数据是假的)
结果
报错类型从 not executable → TYPE_MISMATCH → 正常返回,说明 UDF 已修复。
问题 15:告警详情都是公司的实例地址#
现象
SigNoz 告警通知里「查看详情」链接指向 http://OLD-HOST-1:8086/...,不是本环境的实例地址,浏览器打不开。
根因
告警规则的 annotations.description / annotations.summary 里写死了非本环境地址的 HTML:
<a href="http://OLD-HOST-1:8086/metrics-explorer/explorer?compositeQuery=...">查看详情</a>
处理
通过 SigNoz API 批量更新 24 条规则:
PUT /api/v2/rules/{id} → 204
将 OLD-HOST-1:8086 全部替换为 SIG-HOST:8086,即公司在本环境的 SigNoz 实例地址(A 区,浏览器可达)。
验证:残留 0 处。
问题 16:CMDB 初始数据需替换为公司实际环境#
现象
服务器 / 数据库 / 中间件 / 应用 / 接口均为初始化示例数据,不是乌兰察布实际环境。
识别特征(后续导入数据时用于判断是否为示例数据)
| 特征 | 值 |
|---|---|
create_by | 88888888888888888888888888888888 |
| 接口路径含 | guizhou |
| IP 段 | 10.108.x.x(历史环境网段) |
| 服务名 | INSPUR-DZZW-CJSXGC-V6、TraceDemo-service |
| 主机名 | SRV-202401001 ~ SRV-202401004 |
处理
全部替换为乌兰察布真实数据:
| 资源类型 | 数量 | 来源 |
|---|---|---|
| 服务器 | 9 台 | BIZ-HOST-1/.113/.114/.115/.116/.127、BIZ-HOST-3/.238/.241 |
| 数据库 | 3 个 | MySQL 116:3306 / Oracle 116:1521 / ClickHouse 248.238:8123 |
| 中间件 | 5 个 | Nacos 115:8848 / Redis 115:6379 / Nginx 115:80 / Tomcat 115:8080 / OTel 248.238:4317 |
| 业务应用 | 16 个 | Oracle BSP.PUB_APPS |
| 接口 | 138 个 | ClickHouse 真实 HTTP 路由 |
备份表(可回滚)
cmdb_compute_resource_bak20261027
cmdb_db_resource_bak20261027
cmdb_comp_resource_bak20261027
cmdb_application_bak20261027
cmdb_app_service_bak20261027b
cmdb_app_interface_bak20261027b (709 行,span 名)
cmdb_app_interface_bak20261027c (709 行)
问题 17:接口表唯一键冲突#
现象
Duplicate entry 'xxx-/Inspur.DZZW.XZSP.APPROVE_EXPORTALREQ-POST-0' for key 'uk_app_interface'
根因
唯一键 uk_app_interface (app_id, api_path, method, del_flag) 在 MySQL 默认排序规则下大小写不敏感,
Oracle 表名的大小写变体(APPROVE_EXPORTALREQ / approve_exportalreq)会被判定为重复。
处理
按 (app_id, LOWER(api_path), method) 去重,727 → 709,再用 INSERT IGNORE 入库。
问题 18:nacos\bin\logs 占用 17.4 GB#
现象
D:\server\nacos = 17,551 MB
├── bin 17,411 MB ← ★
├── target 77 MB
└── logs 59 MB
根因
nacos\bin\logs\ 里全是 2021 年 的 Tomcat access log,每天一个 89.9 MB:
access_log.2021-12-08.log 89.9 MB
access_log.2021-11-19.log 89.9 MB
...
属于上一套系统在这台机器上运行遗留的垃圾(2021 年 8–12 月)。
处理
删除 2024 年以前的文件(保留近期以防有用):
Get-ChildItem "D:\server\nacos\bin\logs" -File |
Where-Object { $_.LastWriteTime -lt (Get-Date "2024-01-01") } |
Remove-Item -Force
结果:D 盘已用从 151.6 GB → 140.16 GB,释放约 11.4 GB。
问题 19:SigNoz 服务器时钟偏差 29 分钟#
现象
接口监控「最近 30 分钟」的数字不随新调用变化:调 60 次仍只显示 3 次。链路监控、指标监控、日志监控选择「最近 5 分钟」同样查不到最新数据。
根因
SigNoz 服务器 SIG-HOST 的系统时钟比实际慢 29 分钟,硬件时钟(RTC)正常:
Local time: Tue 2026-10-27 17:08:43 CST ← 系统时钟,慢 29 分钟
Universal time: Tue 2026-10-27 09:08:43 UTC
RTC time: Tue 2026-10-27 09:37:37 ← 硬件时钟,正确
NTP enabled: no
NTP synchronized: no
页面 SQL 为 WHERE timestamp BETWEEN (now()-30m) AND now(),其中 now() 取自 SigNoz 系统时钟。时间窗上界因此落在实际时间 29 分钟之前,窗口内只剩 29~59 分钟前的历史数据。
验证方法
同一接口两种写法对比,结果相差 3 倍:
| 写法 | 结果 |
|---|---|
timestamp > now() - 30m(不设上界) | 15 条 |
timestamp BETWEEN now()-30m AND now()(页面写法) | 5 条 |
三方时间对比(UTC):
| 来源 | 时间 | 说明 |
|---|---|---|
| 应用服务器(写入 span) | 09:37:29 | 正常 |
| 运维终端 | 09:37:19 | 正常 |
| SigNoz 服务器 | 09:08:22 | 慢 29 分钟 |
处理
# 1) 确认硬件时钟正常
timedatectl status
# 2) 从硬件时钟同步系统时钟(该服务器无外网,无需安装 NTP 软件)
hwclock --hctosys
# 3) 确认
date
验证
ClickHouse now() : 09:40:29
终端 UTC : 09:40:32
偏差 : 0.1 分钟
两种查法结果一致(14 = 14),最近 5 分钟数据实时可见,页面刷新后调用总次数正常增长。
影响范围
该偏差使所有基于时间窗的功能整体偏移:
| 功能 | 表现 |
|---|---|
| 接口监控调用总次数 | 显示 29~59 分钟前的数据 |
| 接口调用总览图表 | 时间轴与实际不符 |
| 链路 / 指标 / 日志监控「最近 N 分钟」 | 查不到最新数据 |
| 告警计算窗口 | 告警延迟约 29 分钟 |
| 按天分区边界 | 边界错位 |
预防
该服务器未启用 NTP(NTP enabled: no),时钟会持续漂移,29 分钟量级说明已漂移数周。建议:
- 配置内网 NTP 源,启用 chrony 自动同步:
yum install -y chrony vi /etc/chrony.conf # server <内网NTP地址> iburst systemctl enable --now chronyd chronyc makestep - 若暂无 NTP 源,用计划任务定期从 RTC 对齐:
0 3 * * * /sbin/hwclock --hctosys - 将「服务器时钟偏差」列入日常巡检项,与 ClickHouse 写入量一并检查。
问题 20:性能问题(部署期间顺带发现)#
| 接口 / 组件 | 数据 | 问题 |
|---|---|---|
QYSL/enterprise/business/mainpage | 8 次,平均 33,099 ms(33 秒) | 严重慢,需查 span 明细 |
QYSL/enterprise/business/havedone/query | 1 次,2,969 ms | 慢 |
XZSP/approval/workextend | 1,671 次,平均 1,551 ms | 高频慢接口 |
XZSP/approval/worktodolist/businessOpinion | 1 次,964 ms | 慢 |
QYSL/web/enterprise/updateBizState | 26 次,870 ms | 慢 |
SELECT orcl.WEBFINAL_APP_CONFIG | 48,044 次 / 6 小时(133 次/分) | 配置表无缓存,建议加 Caffeine,TTL 30s |
HTTP 状态码 null | 7,389 / 7,534 | OTel agent 未采集到大部分 http.status_code,导致错误率统计偏低 |
第二部分 修改了哪些文件、思路与代码解释#
2.1 修改文件总览#
| # | 文件 | 类型 | 改动要点 |
|---|---|---|---|
| 1 | D:\server\dgov-obs-business-service\application-dev.yaml | 配置 | 换库地址、关 Kafka、删多余数据源 |
| 2 | D:\server\dgov-obs-resource-service\application-dev.yaml | 配置 | 同上 |
| 3 | D:\server\apache-tomcat-9.0.87-*\bin\setenv.bat × 3 | 新增 | 加 OTel + JMX 探针 |
| 4 | D:\server\nginx\conf\nginx.conf | 配置 | 加 /dms/、/posthog/、禁缓存 |
| 5 | D:\server\nginx\html\dms\index.html | 新增 | 工单占位页 |
| 6 | D:\server\nginx\html\posthog\index.html | 新增 | 行为分析占位页 |
| 7 | D:\server\nginx\html\obs\index.html | 配置 | 改配置版本号 |
| 8 | D:\server\nginx\html\obs\config.signoz.js | 配置 | 覆盖初始地址 |
| 9 | D:\server\nginx\html\obs\config.dms.js | 配置 | 工单地址改本地 |
| 10 | D:\server\nginx\html\obs\js\index-B14RlMeI.js | 前端 | 服务名改真实值 |
| 11 | D:\server\nginx\html\obs\js\signozService-Cg3RwBMu.js | 前端 | SQL http.target → http.route |
| 12 | D:\server\apache-tomcat-9.0.87-WebSite\...\lib\jcore-1.0.jar | 替换 | 用 jcore-3.5.jar 替换 |
| 13 | Nacos dgov-cloud-gateway | 配置 | tokenWhiteList 加 6 条 |
| 14 | SigNoz 告警规则(24 条) | 数据 | 替换初始地址 |
| 15 | ClickHouse UDF 权限 | 系统 | chmod +x histogramQuantile |
| 16 | cmdb_* 各类资源表 | 数据 | 换真实数据 |
2.2 application-dev.yaml(业务服务,8089)#
原始配置#
server:
port: 8089
servlet:
context-path: /obs/business
spring:
cloud:
dubbo:
enabled: false
stream: # Kafka 启用
defaultBinder: kafka
kafka:
binder:
brokers: OLD-HOST-KAFKA:9092
bindings:
signozAlertProducer-out-0:
destination: dsm.oms.sync
datasource:
dynamic:
primary: primary
public-key: MFwwDQYJKoZIhvcNAQEB... # 用于解密 ENC() 密码
datasource:
primary: # MySQL(青岛环境)
url: jdbc:mysql://EXT-HOST-1:3306/dgov-obs-behavior
username: qingdao
password: ENC(aa1VORxQvP/8jx33k9dgPUOs88DMVh5ihPYkwWq7ashZFl6mULgM/8JUMps6A3IhIdvQ6tfZA5MZku8kGg0M5w==)
teledb: # TiDB
url: jdbc:mysql://EXT-HOST-2:3307/dgov-obs-behavior
username: dgov_admin
highgo: # 瀚高数据库
url: jdbc:highgo://EXT-HOST-3:5866/dgov_obs_behavior
username: sysdba
clickhouse:
url: jdbc:ch://OLD-HOST-2:8123/default
signoz:
server: http://OLD-HOST-1:8086
queryUri: /api/v5/query_range
messageCenter:
url: http://localhost:8192/messagecenter/sendSMSMsg
template: "【系统告警】收到 Signoz 告警: {alerts}"
phone: "13800138000"
现网配置(当前)#
server:
port: 8089
servlet:
context-path: /obs/business
spring:
cloud:
dubbo:
enabled: false # 原本就是 false,未改动
# Kafka disabled (no Kafka in this site).
# stream: # ← 整块注释
# ... brokers: 127.0.0.1:9092
datasource:
dynamic:
primary: primary
datasource:
primary:
url: jdbc:mysql://DB-HOST-B:3306/dgov-obs-behavior?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: dgov_obs
password: '<dgov_obs-密码>'
driver-class-name: com.mysql.cj.jdbc.Driver
clickhouse:
url: jdbc:ch://SIG-HOST-B:8123/default?compress=true&async=1
username: default
driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
hikari:
max-pool-size: 10
connection-timeout: 30000
idle-timeout: 600000
signoz:
user: your-email@example.com
password: '<SigNoz-密码>'
server: http://SIG-HOST-B:8086
tokenUri: /api/v2/sessions/email_password
orgUri: /api/v2/sessions/context
loginUri: /login
queryUri: /api/v5/query_range
messageCenter:
url: http://127.0.0.1:8192/messagecenter/sendSMSMsg
template: "Signoz Alert: {alerts}"
phone: "13800138000"
smsChannelId: "signoz"
逐项改动与理由#
| # | 配置项 | 原值 | 现在 | 理由 |
|---|---|---|---|---|
| 1 | spring.cloud.stream 整块 | 启用 | 注释 | 本环境无 Kafka。启用会不停重连 OLD-HOST-KAFKA:9092,刷错误日志 |
| 2 | datasource.primary.url | EXT-HOST-1:3306 | DB-HOST-B:3306 | 换成本地 MySQL(B 区地址,115 可达) |
| 3 | datasource.primary.username | qingdao | dgov_obs | 本地库账号 |
| 4 | datasource.primary.password | ENC(...) 密文 | 明文 | 密文需 public-key 解密,换库后直接用明文更简单 |
| 5 | datasource.public-key | 有 | 删除 | 不再用 ENC(),公钥无用 |
| 6 | datasource.teledb | 有(TiDB) | 删除 | 本环境无 TiDB |
| 7 | datasource.highgo | 有(瀚高) | 删除 | 本环境无瀚高 |
| 8 | datasource.clickhouse.url | OLD-HOST-2:8123 | SIG-HOST-B:8123 | SigNoz 的 ClickHouse |
| 9 | signoz.server | OLD-HOST-1:8086 | SIG-HOST-B:8086 | 真实 SigNoz 地址(服务端走 B 区) |
| 10 | messageCenter.url | localhost:8192 | 127.0.0.1:8192 | 等价 |
| 11 | messageCenter.template | 中文模板 | 英文模板 | 避免编码问题 |
配置含义解释#
server
port: 8089 # 服务监听端口(对应 java 进程 PID 12316)
context-path: /obs/business # URL 前缀 → 所有接口是 /obs/business/xxx
spring.cloud.dubbo.enabled: false
本服务不用 Dubbo(只访问 MySQL + ClickHouse + SigNoz HTTP API)。
若改 true,启动时会去找 Dubbo 注册中心(ZooKeeper/Nacos),找不到就报错。
spring.cloud.stream(Kafka)
原设计链路:
SigNoz 告警 → Webhook → OBS 业务服务 → Kafka topic: dsm.oms.sync → DMS 工单系统 → 自动建工单
本环境无 Kafka,该链路断开。告警→工单目前只能靠 HTTP webhook 直连。
datasource.dynamic(多数据源)
使用 dynamic-datasource 组件,primary: primary 表示 @DS 不指定时默认用 primary。
原设计兼容 4 种数据库(MySQL / TiDB / 瀚高 / ClickHouse),本环境只用 MySQL + ClickHouse。
datasource.clickhouse
url: jdbc:ch://.../default # default 库是空的,代码里 SQL 都写了全名 signoz_traces.xxx 才能用
compress=true # LZ4 压缩
async=1 # 异步插入
hikari.max-pool-size: 10 # 连接池上限
signoz
| 键 | 含义 |
|---|---|
user / password | SigNoz 登录账号,用于换 token |
server | SigNoz 后端地址 |
tokenUri | 登录接口 /api/v2/sessions/email_password |
orgUri | 组织信息接口(SigNoz 部分 API 需要 orgID) |
loginUri | 网页登录页 |
queryUri | 查询接口 /api/v5/query_range:SignozProxyController 转发时用 |
messageCenter
url: http://127.0.0.1:8192/messagecenter/sendSMSMsg # 短信网关
template: "Signoz Alert: {alerts}" # 短信内容,{alerts} 被替换为告警 JSON
phone: "13800138000" # 收件手机号(占位假号)
smsChannelId: "signoz" # 短信通道 ID
遗留风险(4 项)#
teledb/highgo删除后:若代码中有@DS("teledb")/@DS("highgo"),运行时会抛异常。需确认代码是否引用。messageCenter指向的127.0.0.1:8192服务不存在:短信告警必然失败。- 密码明文存储:原设计用
ENC()+public-key加密,现为明文。安全测评会被提。 clickhouse连default空库:任何不带库名前缀的查询都会 404。建议改为signoz_traces。
2.3 setenv.bat(探针配置,新增 3 个)#
文件位置#
D:\server\apache-tomcat-9.0.87-ACCEPT\bin\setenv.bat (综合受理,8010)
D:\server\apache-tomcat-9.0.87-Parallel\bin\setenv.bat (并联审批,8068)
D:\server\apache-tomcat-9.0.87-WebSite\bin\setenv.bat (网办后台,8030)
XZSP(8090)原本已有,作为模板。
内容(以 Parallel 为例)#
@echo off
set "JAVA_OPTS=%JAVA_OPTS% -javaagent:D:\app\jmx_prometheus\jmx_prometheus_javaagent-0.15.0.jar=9997:D:\app\jmx_prometheus\config.yaml"
set "JAVA_OPTS=%JAVA_OPTS% -javaagent:D:\app\opentelemetry-javaagent\opentelemetry-javaagent.jar"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.service.name=INSPUR-DZZW-BLSP"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.exporter.otlp.endpoint=http://SIG-HOST-B:4318"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.exporter.otlp.protocol=http/protobuf"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.traces.exporter=otlp"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.resource.attributes=app.code=INSPUR-DZZW-BLSP,deployment.environment=production,environment=production"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.traces.sampler=parentbased_traceidratio"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.traces.sampler.arg=1.0"
set "JAVA_OPTS=%JAVA_OPTS% -Dotel.logs.exporter=none"
逐行解释#
| 行 | 含义 |
|---|---|
@echo off | 关闭命令回显 |
-javaagent:jmx_prometheus_javaagent-0.15.0.jar=9997:config.yaml | JMX 探针,在 9997 端口暴露 JVM 指标(堆内存/GC/线程/连接池) |
-javaagent:opentelemetry-javaagent.jar | OTel 探针,自动给 Spring/JDBC/Dubbo/HTTP 调用打埋点,生成 Trace |
-Dotel.service.name=INSPUR-DZZW-BLSP | 服务名,SigNoz 里按这个分组 |
-Dotel.exporter.otlp.endpoint=http://SIG-HOST-B:4318 | 上报地址(B 区 OTel Collector,走 HTTP/4318) |
-Dotel.exporter.otlp.protocol=http/protobuf | 传输协议 |
-Dotel.traces.exporter=otlp | 只导出 Trace(不导 metrics/logs) |
-Dotel.resource.attributes=app.code=...,deployment.environment=production | 资源属性,便于筛选 |
-Dotel.traces.sampler=parentbased_traceidratio | 采样策略:按 traceID 比例 |
-Dotel.traces.sampler.arg=1.0 | 采样率 100%(低流量系统全采才有意义) |
-Dotel.logs.exporter=none | 不采集日志(避免重复采集、避免量太大) |
关键点:JMX 端口必须唯一#
| Tomcat | 端口 | JMX |
|---|---|---|
| XZSP | 8090 | 9999(原有) |
| Parallel | 8068 | 9997 |
| ACCEPT | 8010 | 9998 |
| WebSite | 8030 | 9996 |
端口冲突会导致第二个 Tomcat 起不来。
编码规范(曾经遇到)#
- 必须 CRLF 换行
- 必须纯 ASCII(中文注释会导致
'xxx' 不是内部或外部命令)
2.4 nginx.conf#
完整结构#
worker_processes 1;
error_log logs/error.log warn;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
client_max_body_size 100m;
server {
listen 80;
server_name _;
# ① 前端静态文件
location /obs/ {
alias D:/server/nginx/html/obs/;
index index.html;
try_files $uri $uri/ /obs/index.html;
}
# ② 网关
location /gate/ {
proxy_pass http://127.0.0.1:30901/gate/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
# ③ BSPPLUS
location /ucenter/ {
proxy_pass http://127.0.0.1:9099/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
}
# ④ 行为分析占位页
location /posthog/ {
alias D:/server/nginx/html/posthog/;
index index.html;
try_files $uri $uri/ /posthog/index.html;
}
# ⑤ 工单系统:模拟「未找到工单」
location = /dms/local/dsm-plugin-oms/api/order/getInfo {
default_type 'application/json; charset=utf-8';
add_header Cache-Control 'no-store';
return 200 '{"state":0,"message":"TkOrder not found: DMS not connected"}';
}
# ⑥ 工单占位页
location ^~ /dms/ {
alias D:/server/nginx/html/dms/;
index index.html;
try_files $uri $uri/ /dms/index.html;
}
# ⑦ 前端配置文件禁缓存
location ~ ^/obs/config\.[a-z0-9]+\.js$ {
root D:/server/nginx/html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
expires -1;
}
# ⑧ 根路径
location = / { return 302 /obs/; }
location / {
root D:/server/nginx/html;
index index.html;
}
}
}
各 location 解释#
| # | location | 作用 | 关键点 |
|---|---|---|---|
| ① | /obs/ | 前端静态文件 | alias 而不是 root;try_files 兜底到 index.html(Vue history 路由需要) |
| ② | /gate/ | 转发到网关 30901 | proxy_pass 带 /gate/ 路径,网关侧再做 StripPrefix |
| ③ | /ucenter/ | 转发到 BSPPLUS 9099 | 登录、菜单、SSO |
| ④ | /posthog/ | 行为分析占位页 | PostHog 未部署 |
| ⑤ | = /dms/…/getInfo | 精确匹配,直接返回 JSON | = 优先级最高;返回 TkOrder not found 让前端走友好分支 |
| ⑥ | ^~ /dms/ | 工单占位页 | ^~ 表示前缀匹配后不再检查正则 |
| ⑦ | ~ ^/obs/config\.…js$ | 配置文件禁缓存 | 正则匹配,比前缀匹配优先级高(除 ^~ 和 =) |
| ⑧ | = / | 根路径重定向到 /obs/ | — |
location 匹配优先级(Nginx 规则)#
1. = /exact 精确匹配,最高优先级
2. ^~ /prefix 前缀匹配,命中后不再检查正则
3. ~ regex 正则匹配(按配置文件中的顺序,第一个命中的生效)
4. /prefix 普通前缀匹配,选最长匹配
曾经遇到的问题#
第一次插入 /posthog/ 时放错了大括号层级(放到了 server{} 外面),
导致 nginx -t 报错、nginx -s reload 静默失败、页面一直 404。
做法:每次改完必须 nginx -t 验证,再 -s reload。
2.5 前端 Js 补丁(3 个文件)#
2.5.1 index-B14RlMeI.js — 服务名#
改前
serviceName: "INSPUR-DZZW-CJSXGC-V6" // 公司「一件事一次办 V6」,本环境不存在
改后
serviceName: "INSPUR-DZZW-XZSP" // 通用审批(真实在跑,流量最大)
思路
service.name 是 SigNoz 的服务过滤条件,代码里写死了示例服务名 → 查询 0 条。
换成本环境真实服务名。
遗留问题:这个文件里 serviceName 出现 2 次(查询 + 详情路由跳转),都已替换。
2.5.2 signozService-Cg3RwBMu.js — SQL 修正#
改前
AND attributes_string['http.target'] IN $http.route
改后
AND attributes_string['http.route'] IN $http.route
完整 SQL(改后)
SELECT
attributes_string['http.route'] AS `接口地址`,
toFloat64(count()) AS `调用总次数`,
toFloat64(countIf(toFloat64OrNull(response_status_code) >= 400)) AS `错误次数`,
if(
toFloat64(count()) > 0,
(toFloat64(countIf(toFloat64OrNull(response_status_code) >= 400)) / toFloat64(count()) * 100),
0
) AS `错误率(%)`,
if(
toFloat64(count()) > 0,
if(
(toFloat64(countIf(toFloat64OrNull(response_status_code) >= 400)) / toFloat64(count()) * 100) > 10,
'异常', '正常'
),
'无流量'
) AS `接口状态`
FROM signoz_traces.distributed_signoz_index_v3
WHERE timestamp BETWEEN {{.start_datetime}} AND {{.end_datetime}}
AND attributes_string['http.scheme'] = 'http'
AND attributes_string['http.target'] NOT LIKE '%/static/%'
AND attributes_string['http.target'] NOT LIKE '%.css'
AND attributes_string['http.target'] NOT LIKE '%.js'
AND attributes_string['http.target'] NOT LIKE '%.png'
AND attributes_string['http.target'] NOT LIKE '%.ico'
AND attributes_string['http.route'] IN $http.route
GROUP BY `接口地址`
ORDER BY `调用总次数` DESC
思路
http.target = 完整请求路径(含 query string);http.route = 路由模板(不含参数)。
前端传的是路由模板(/xxx/yyy),必须用 http.route 匹配才命中。
这个 SQL 是通过 POST /obs/business/signoz/proxy/api/v5/query_range 转发的,
body 里带 type: "clickhouse_sql"。当前网关返回 非法参数,此功能仍未生效:见问题 14。
2.6 config.dms.js(工单地址本地化)#
改前
DMS_APP_KEY: "",
DMS_APP_TOKEN: "",
DMS_API_ROOT: "https://dms.example.com",
DMS_DETAIL_PAGE_URL: "https://dms.example.com/dsm/ticket/work/detail",
DMS_CREATE_PAGE_URL: "https://dms.example.com/dsm/ticket/work/addProblem?code=CATA20260707115553259&backPath=%252Fwork%252Fsubmit",
DMS_ORDER_INFO_PATH: "/local/dsm-plugin-oms/api/order/getInfo",
DMS_HTTP_TIMEOUT: 30000
改后
DMS_API_ROOT: '/dms',
DMS_DETAIL_PAGE_URL: '/dms',
DMS_CREATE_PAGE_URL: '/dms',
DMS_HTTP_TIMEOUT: 5000
思路
- 改同源相对路径 → 消灭 DNS 解析和外网等待
- 超时 30s → 5s → 快速失败
- 配合 Nginx 返回
TkOrder not found→ 前端走「尚未生成工单 → 创建工单」的友好分支
前端逻辑链
POST {DMS_API_ROOT}{DMS_ORDER_INFO_PATH}
↓ state !== 1 且 message 含 "TkOrder not found"
→ err.code = "TK_ORDER_NOT_FOUND"
↓
confirm("该告警尚未生成工单,是否前往创建工单?")
↓ 点确定
window.open(DMS_CREATE_PAGE_URL) // → 打开 /dms 占位页
2.7 config.signoz.js(覆盖历史地址)#
思路
前端 env-xH10ADq8.js 里烘焙了历史环境地址:
VITE_SIGNOZ_BASE_URL: "http://OLD-HOST-1:3301"
VITE_SIGNOZ_API_URL: "http://OLD-HOST-1:8086"
VITE_APP_API_URL: "http://OLD-HOST-3:31225/gate"
VITE_APP_OAUTH2_SERVER_URL: "http://OLD-HOST-3:32520/ucenter"
配置解析优先级:
window.posthogConfig → window.publicConfig → window.signozConfig → 烘焙默认值 I[key]
因此在 config.signoz.js 里设置 window.signozConfig 即可覆盖烘焙值,无需改打包产物。
关键设计:双网卡地址不能填错
| 调用方 | 用哪个地址 | 原因 |
|---|---|---|
| 浏览器(前端) | SIG-HOST:8086(A 区) | 我的电脑在 A 区 |
| 服务端(后端/探针) | SIG-HOST-B:4318/8086(B 区) | 115 与 B 区互通,且 OTel 上报已验证可用 |
填反了会导致:前端打不开 SigNoz 页面,或后端上报不到数据。
2.8 index.html(配置版本号)#
改前
<script src="./config.js?id=1792120301403"></script>
<script src="./config.signoz.js?id=1792120301403"></script>
<script src="./config.posthog.js?id=1792120301403"></script>
<script src="./config.dms.js?id=1792120301403"></script>
改后
<script src="./config.js?id=202610271345"></script>
...(4 个文件全部更换 id)
思路
?id= 是打包工具生成的固定版本号,浏览器据此判断缓存是否有效。
不换 id,改了 config.*.js 也不会生效。
根治办法:Nginx 对 config.*.js 加 no-store(见 2.4 第 ⑦ 条),以后改配置不用再换 id。
2.9 jcore-1.0.jar → jcore-3.5.jar(网办后台)#
操作
$lib = "D:\server\apache-tomcat-9.0.87-WebSite\webapps\iWebSite\WEB-INF\lib"
# 备份
Copy-Item "$lib\jcore-1.0.jar" "$lib\jcore-1.0.jar.bak-<时间戳>"
# 从 XZSP 取新版
Copy-Item "...\XZSP\webapps\Inspur.Dzzw.WebApproval\WEB-INF\lib\jcore-3.5.jar" "$lib\jcore-3.5.jar"
# 旧版改名禁用(不是删除,可回滚)
Move-Item "$lib\jcore-1.0.jar" "$lib\jcore-1.0.jar.disabled-<时间戳>"
# 重启
回滚
Move-Item "$lib\jcore-1.0.jar.disabled-*" "$lib\jcore-1.0.jar"
Move-Item "$lib\jcore-3.5.jar" "$lib\jcore-3.5.jar.unused"
思路
这不是"破解授权",而是把漏升级的组件升到公司当前版本(其他 3 个产品都在用 jcore-3.5)。
风险在 API 兼容性,不在合规性。已验证 jcore-1.0 → jcore-3.5 兼容。
待办:仍需公司(成哥)确认此替换是被支持的,并排查其他服务器上的应用是否有同样问题。
2.10 Nacos 网关配置(dgov-cloud-gateway)#
改前
security:
custom:
tokenWhiteList:
- /gate/bspplus/utils/uuid
# ...(原有条目)
改后(追加 6 条)
security:
custom:
tokenWhiteList:
- /gate/bspplus/utils/uuid
- /gate/bspplus/utils/** # 工具类(uuid、滑块验证码等)
- /gate/bspplus/user/login # 登录
- /gate/bspplus/user/signOn/login # 单点登录
- /gate/bspplus/oauth/** # OAuth
- /gate/bspplus/sso/** # SSO
- /gate/bspplus/user/logout # 退出
思路
GlobalAuthorizationFilter 用 AntPathMatcher 匹配,支持 ** 通配。
白名单只放登录/工具/SSO 这类必需免登录的路径,不放业务接口。
注意:anon-white-list 在本环境不生效(因为 api-auth-check-enable: false 关掉了对应过滤器),不要再往那里加。
2.11 数据库改动#
2.11.1 CMDB 资源表(换真实数据)#
| 表 | 现网行数 | 备份表 |
|---|---|---|
cmdb_compute_resource(服务器) | 9 | _bak20261027 |
cmdb_db_resource(数据库) | 3 | _bak20261027 |
cmdb_comp_resource(中间件) | 5 | _bak20261027 |
cmdb_application(应用) | 16 | _bak20261027 |
cmdb_app_service(应用服务) | 9 | _bak20261027b |
cmdb_app_interface(接口) | 138 | _bak20261027b(709) / _bak20261027c(709) |
回滚方式(以接口表为例)
DELETE FROM `dgov-obs-behavior`.cmdb_app_interface;
INSERT INTO `dgov-obs-behavior`.cmdb_app_interface
SELECT * FROM `dgov-obs-behavior`.cmdb_app_interface_bak20261027c;
2.11.2 cmdb_app_interface 实际表结构#
id varchar(64) NOT NULL
app_id varchar(64) NOT NULL
service_id varchar(64) NULL
api_name varchar(255) NOT NULL
api_path varchar(500) NOT NULL
method varchar(20) NULL
type int(11) NOT NULL -- 1 = HTTP 接口
remark varchar(500) NULL
create_time datetime NULL
update_time datetime NULL
create_by varchar(64) NULL
update_by varchar(64) NULL
del_flag int(11) NULL -- 0 = 有效
is_high_risk int(11) NOT NULL -- 1 = 高危(默认筛选项)
注意:没有 app_name 列(曾经遇到:按 app_name 插入会报 ERROR 1054)。应用名通过 app_id 关联 cmdb_application 查。
2.12 SigNoz 侧改动#
2.12.1 告警规则(24 条)#
- 改动内容:
annotations.description/annotations.summary里的 HTML 链接 - 改前:
http://OLD-HOST-1:8086/... - 改后:
http://SIG-HOST:8086/... - API:
PUT /api/v2/rules/{id}→ 204 - 验证:残留 0 处
2.12.2 ClickHouse UDF 权限#
docker exec -u root signoz-clickhouse chmod +x /var/lib/clickhouse/user_scripts/histogramQuantile
说明:docker exec 不做 shell 通配符展开,用 chmod +x /path/* 会报
cannot access '/path/*': No such file or directory。必须写完整文件名,或加 bash -c。
写在最后#
这 20 个问题里,只有 3 个是"配置写错了一个参数"。剩下的要么是信息藏在二进制里(jar 里的硬编码日期),要么是跨越了多个组件(网关拦了、前端 SQL 不对、服务器时钟还偏),要么是报错信息完全误导(应用启动失败但日志只说 not initialize)。
几点体会:
1. 报错信息不可信的时候,去看底层。
“启动即退出"看起来像配置问题、像网络问题、像授权问题,实际是 jar 里一个硬编码的日期字符串。这种信息,配置文件里永远找不到,只能反编译 class 看常量池。
2. 现象和根因之间可能隔着好几层。
“调用次数是 0"这一个现象,背后是三个独立的问题:SQL 匹配字段不对、网关的安全策略拦了查询请求、服务器时钟偏移把时间窗切掉了。修完前两个数字能出来了,但只出来一部分——第三个问题才让数字完全对上。
3. 时间基准是所有监控的地基。
可观测平台的所有查询都是"某段时间内的数据”。如果服务器时钟偏了 29 分钟,那所有"最近 N 分钟"的视图都在看历史数据,所有告警都会延迟。这个问题很难从界面上看出来,因为界面上的数字"看起来是正常的”,只是永远不更新。
4. 文档里不会写的,才是真正花时间的地方。
公司部署手册写的都是"配置这一项、启动服务、访问地址"。真实环境里的问题,是 License 过期、jar 版本不一致、bat 文件编码、双网卡地址填反这一类。这些只能靠现场踩。
如果这篇能让你少踩两个坑,那就值了。
本文所有 IP、账号、密码、域名均已脱敏,仅保留技术细节。
附录 A 环境信息速查#
主机与地址#
| 用途 | A 区 | B 区 |
|---|---|---|
| 应用服务器 | APP-HOST | APP-HOST-B |
| 可观测平台服务器 | SIG-HOST | SIG-HOST-B |
| MySQL | DB-HOST:3306 | DB-HOST-B:3306 |
| Oracle | DB-HOST:1521 | — |
服务端口#
| 服务 | 端口 | 进程 |
|---|---|---|
| Nginx | 80 | — |
| 网关 | 30901 | java |
| BSPPLUS | 9099 | java |
| obs-resource-service | 8087 | java |
| obs-business-service | 8089 | java |
| Nacos | 8848 | java |
| Tomcat XZSP | 8090 | java |
| Tomcat ACCEPT | 8010 | java |
| Tomcat Parallel | 8068 | java |
| Tomcat WebSite | 8030 | java |
| JMX 探针 | 9996/9997/9998/9999 | — |
关键凭据#
| 项 | 值 |
|---|---|
| 平台登录 | admin / <平台管理员密码> |
| MySQL root | <MySQL-root-密码> |
MySQL dgov_obs | <dgov_obs-密码> |
MySQL dgov_bsp | <dgov_bsp-密码> |
| SigNoz | your-email@example.com / <SigNoz-密码> |
| SigNoz orgID | <org-id> |
访问地址#
| 用途 | 地址 |
|---|---|
| 平台前端 | http://APP-HOST/obs/#/login |
| SigNoz(浏览器) | http://SIG-HOST:8086 |
| ClickHouse HTTP | http://SIG-HOST:8123 |
附录 B 经验总结#
B.1 Windows 脚本类#
| 问题 | 现象 | 正确做法 |
|---|---|---|
.bat 用 LF 换行 | 'xxx' 不是内部或外部命令、中文碎片被执行 | 必须 CRLF |
.bat 含中文 | cmd 解析乱码 | 纯 ASCII,注释也用英文 |
| 管道接驻留进程 | 命令行永久挂住 | Start-Process -WindowStyle Hidden,不接管输出 |
docker exec 用通配符 | cannot access '/path/*' | 写完整路径,或 bash -c 'cmd *' |
B.2 前端补丁类#
| 问题 | 正确做法 |
|---|---|
改了 config.*.js 不生效 | 换 ?id= 版本号 + Nginx 对 config 禁缓存 |
报错被 try/catch 吞掉 | 我排查时先看 Console 报错,不要只看界面 |
B.3 排查方法类#
| 场景 | 方法 |
|---|---|
| 不知道某接口/某功能怎么实现的 | 反编译 jar(javap -p -c),优先采用 |
| 不知道前端调的什么接口 | F12 → Network,看 URL + Payload + Response |
| 不知道请求被谁拒绝 | 看响应字段名:message = 业务服务,msg = 网关 |
| 不知道数据为什么查不到 | 直连底层数据源验证(ClickHouse / MySQL),再逐层往上看 |
| 大文件 / 大目录占用 | 按目录逐层统计(一级目录 → 子目录 → 单个文件) |
| 服务重启后起不来 | 先看 jar 里有没有硬编码的日期/授权检查(javap -p -constants) |
| 数据「看起来没有采集」 | 先核对各服务器时钟:date -u 逐台对比;时钟偏差会使 BETWEEN now()-N AND now() 的时间窗整体偏移 |
B.4 判断是否为本次改动引入的问题#
| 现象 | 判断依据 |
|---|---|
重启后起不来,日志有 DateUtils.compareDate | 不是配置问题,是 jar 里硬编码到期日 |
| 换配置后还是不生效 | 先确认文件真的被加载了(浏览器缓存 / 服务是否重启) |
| 功能一直返回空 | 先直连数据源验证数据存在,再排除中间层 |
文档结束
