Java 监控
本页用于内嵌 Spring Boot Admin(SBA)控制台,观察后端 JVM 的内存、线程、日志级别等运行时指标。当前部署下打开它会看到一段 401 JSON——原因是后端压根没有把 SBA 打进可运行的 jar,控制台不存在。本页把现状、真正的原因与两条能走通的路讲清。
本页此前的诊断是错的,已更正
旧版这里写的是「后端已内置 SBA 服务端与客户端,监控台本身是活的,只是安全策略没放行 /admin/**」,并给出解法 registry.requestMatchers("/admin/**").permitAll()。 这个诊断不成立,那条解法也修不好任何东西——照做之后 401 会变成 404({"code":404,"msg":"请求地址不存在:admin/applications"}),因为路径背后没有服务。而两者的 HTTP 状态码都是 200(本系统对所有错误都返回 200,真实码在响应体的 code 字段里),所以从状态码上完全看不出区别。
功能在哪
- 菜单路径:基础设施 → 监控中心 → Java 监控
- 路由地址:
/infra/monitors/admin-server - 页面形态:iframe 内嵌 SBA 控制台,地址默认为「后端地址 +
/admin/applications」,可在配置管理中用键url.spring-boot-admin覆盖
当前真实状态:401

iframe 里显示的是后端原样返回的 JSON:
{"code":401,"msg":"账号未登录","data":null}为什么会这样
SBA 的两个 starter 在后端都是 <optional>true</optional> 的依赖:
| 构件 | 声明位置 | 版本 |
|---|---|---|
spring-boot-admin-starter-server | enlev-module-infra/pom.xml | 3.5.9(在 enlev-dependencies 里钉住) |
spring-boot-admin-starter-client | enlev-framework/enlev-spring-boot-starter-monitor/pom.xml | 3.5.9 |
Maven 的 optional 依赖不传递,所以两个都没有进入 enlev-server.jar。可以自己验证:
unzip -l enlev-server/target/enlev-server.jar | grep -i codecentric
# 无输出 = 不在包里于是链条是这样断的:
AdminServerConfiguration上有@ConditionalOnClass(name = "...AdminServerProperties"),类不存在 → 整个配置类不加载;- 它注册的那条
@Order(1)的SecurityFilterChain(表单登录 + HTTP Basic,只匹配/admin/**)从未被创建; /admin/**于是落到主 token 过滤器链上,而它要求Authorization: Bearer ...;- iframe 发的是普通浏览器请求、不带 token →
{"code":401,"msg":"账号未登录"}。
401 是「链条断在第 3 步」的表现,不是安全策略在拦监控台
安全策略这边没有任何针对 /admin/** 的规则——既没有放行,也没有专门拦截。它只是没有被那条本该优先匹配的 @Order(1) 链接管,于是被兜底规则处理了。
两条能走通的路
两条路都必须先把依赖真的加进去,只改安全配置或只改配置键都没用。
路一:让后端自带 SBA 控制台(内网 / 演示环境)
- 把两个 starter 的
<optional>true</optional>去掉,或在enlev-server/pom.xml里显式声明这两个依赖; - 把
spring.boot.admin.*配置加回application-{profile}.yaml(client.url/client.username/client.password/context-path: /admin/frame-ancestors),口令走环境变量、不要写进入库的 yaml; - 修掉
AdminServerConfiguration里@Value("${spring.boot.admin.context-path:''}")的默认值——它的默认值是两个单引号字符而不是空串,context-path缺配时会生成''/**这样的securityMatcher; - 重启后端。此时
/admin/login是 SBA 自己的登录页,本页 iframe 会显示它。
不要顺手加 permitAll("/admin/**")
路一走通之后,/admin/** 由那条 @Order(1) 的链保护,带登录页、需要认证,这是对的。 如果在主链上给它加 permitAll,一旦哪天依赖又变回 optional(那条 @Order(1) 链随之消失),就会静默退化成一个匿名的 JVM 控制台:能看到实例列表、在线改日志级别、抓线程与堆快照,而堆里有 token 与全部生效配置。 另:那是在业务端口 48080 上新开一个鉴权 UI,上线前请单独走一遍安全评审。
路二:指向独立部署的 SBA(生产环境推荐)
单独部署一套带自身登录体系的 Spring Boot Admin,把配置键 url.spring-boot-admin 指向它的地址,本页 iframe 随之加载独立控制台,登录与鉴权由它自己负责,主后端的安全策略无需松动。
⚠️ 但它要能看到本系统这个实例,本系统仍然得作为客户端注册进去——也就是 spring-boot-admin-starter-client 必须在 classpath 上(第 1 步同路一),并配 spring.boot.admin.client.url 指向那台独立 SBA。SBA 是客户端主动注册的模型,服务端不会自己发现应用。
控制台正常时能看什么
上面任一条路走通之后,SBA 控制台提供:
| 板块 | 内容 |
|---|---|
| 实例列表 | 已注册后端实例(enlev-server)及在线状态 |
| 度量 | JVM 堆 / 非堆内存、GC 次数与耗时曲线 |
| 线程 | 线程 dump、阻塞与等待线程排查 |
| 日志级别 | 在线调整包级日志级别,排障时临时开 DEBUG |
| 环境 / 配置属性 | 生效中的配置项视图 |
| http 追踪 | 请求级追踪(需启用对应 actuator 端点) |
生产环境的 actuator 只开了 health 与 info
application-prod.yaml 把 management.endpoints.web.exposure.include 收成了 health,info(原先是 '*',那等于把 /actuator/env 与 /actuator/heapdump 开到公网)。所以即便启用了 SBA,「环境 / 配置属性」与「http 追踪」这两块在 prod 也是空的——这是有意的取舍,不要为了让页面好看而把 '*' 放回去。
日常排障组合拳:MySQL 监控 看 SQL → 本页看 JVM / 线程 → 错误日志 看堆栈。
视角对照
| 视角 | 能看到什么 |
|---|---|
| 任意登录用户(含租户管理员) | 当前部署下均为同一段 401 JSON;按上面任一条路启用后为全局 JVM 指标,不区分租户 |
进阶
- 三个监控页的分工:MySQL 监控 = 数据库层,Redis 监控 = 缓存层,本页 = JVM 层。接口慢时按「SQL → 缓存 → JVM」顺序排除。前两个当前可用,本页不可用。
- 线程 dump 的用法:接口卡死时抓一次 dump,找
BLOCKED/ 长时间RUNNABLE的栈,定位锁竞争或死循环。SBA 没启用时可以用jstack <pid>顶上。 - 日志级别在线调:排障期把业务包调到 DEBUG,复现后调回 INFO——无需重启,但重启后会还原。SBA 没启用时只能改配置重启。
- GC 曲线读法:Full GC 频繁且堆回收后仍高水位,多为内存泄漏或堆配置过小;对照「度量」页 Old 区曲线。
- 配置键覆盖:
url.spring-boot-admin与url.druid一样走配置管理,改完刷新本页即生效,无需重新发版前端。 <optional>true</optional>这个坑的一般形态:声明在 starter 里的 optional 依赖不会随 starter 传递给最终的可执行 jar,于是所有@ConditionalOnClass门控的配置静默失效、对应的 yaml 变成死配置,而启动不报错、日志里没有一行提示。同类情况还有micrometer-registry-prometheus(在 monitor starter 里也是 optional,所以/actuator/prometheus不存在)。排查这类问题的通用手法就是unzip -l看 jar 里到底有没有那个类。
常见问题
为什么别的监控页正常,只有这页是 JSON? Druid 的 StatViewServlet 是随 druid starter 正常传递进来的、自带登录页;SBA 的两个 starter 是 optional、没进 jar。差异来自依赖是否真的在包里,与安全配置无关。
是 bug 吗?要不要报障? 是「功能没被装配起来」,不是运行期故障。当前部署下它就是不可用的;要用的话按上面两条路之一处理,那是一次需要重新发版的改动。
加了 permitAll("/admin/**") 还是不显示? 因为问题不在安全配置。加完之后返回体会从 code:401 变成 code:404,HTTP 状态码则一直是 200,看上去「没变化」。请把依赖加进 jar(路一第 1 步)。
怎么快速确认 SBA 到底在不在包里?unzip -l enlev-server/target/enlev-server.jar | grep -i codecentric。有输出才说明装配上了。
生产环境能用路一吗? 不建议默认开。那是在业务端口上新增一个能看 JVM 全貌、能在线改日志级别的鉴权 UI;生产请用路二的独立控制台,并把它放在内网。
iframe 地址能改成别系统的监控页吗? 可以。配置键 url.spring-boot-admin 指向任意可匿名或自带登录的 HTTP 地址,iframe 即加载它——这也是不改后端就能让本页「有东西看」的唯一办法。
