Skip to content

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 ​

Java 监控当前返回 401

iframe 里显示的是后端原样返回的 JSON:

json
{"code":401,"msg":"账号未登录","data":null}

为什么会这样 ​

SBA 的两个 starter 在后端都是 <optional>true</optional> 的依赖:

构件声明位置版本
spring-boot-admin-starter-serverenlev-module-infra/pom.xml3.5.9(在 enlev-dependencies 里钉住)
spring-boot-admin-starter-clientenlev-framework/enlev-spring-boot-starter-monitor/pom.xml3.5.9

Maven 的 optional 依赖不传递,所以两个都没有进入 enlev-server.jar。可以自己验证:

bash
unzip -l enlev-server/target/enlev-server.jar | grep -i codecentric
# 无输出 = 不在包里

于是链条是这样断的:

  1. AdminServerConfiguration 上有 @ConditionalOnClass(name = "...AdminServerProperties"),类不存在 → 整个配置类不加载;
  2. 它注册的那条 @Order(1) 的 SecurityFilterChain(表单登录 + HTTP Basic,只匹配 /admin/**)从未被创建;
  3. /admin/** 于是落到主 token 过滤器链上,而它要求 Authorization: Bearer ...;
  4. iframe 发的是普通浏览器请求、不带 token → {"code":401,"msg":"账号未登录"}。

401 是「链条断在第 3 步」的表现,不是安全策略在拦监控台

安全策略这边没有任何针对 /admin/** 的规则——既没有放行,也没有专门拦截。它只是没有被那条本该优先匹配的 @Order(1) 链接管,于是被兜底规则处理了。

两条能走通的路 ​

两条路都必须先把依赖真的加进去,只改安全配置或只改配置键都没用。

路一:让后端自带 SBA 控制台(内网 / 演示环境) ​

  1. 把两个 starter 的 <optional>true</optional> 去掉,或在 enlev-server/pom.xml 里显式声明这两个依赖;
  2. 把 spring.boot.admin.* 配置加回 application-{profile}.yaml(client.url / client.username / client.password / context-path: /admin / frame-ancestors),口令走环境变量、不要写进入库的 yaml;
  3. 修掉 AdminServerConfiguration 里 @Value("${spring.boot.admin.context-path:''}") 的默认值——它的默认值是两个单引号字符而不是空串,context-path 缺配时会生成 ''/** 这样的 securityMatcher;
  4. 重启后端。此时 /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 指标,不区分租户

进阶 ​

  1. 三个监控页的分工:MySQL 监控 = 数据库层,Redis 监控 = 缓存层,本页 = JVM 层。接口慢时按「SQL → 缓存 → JVM」顺序排除。前两个当前可用,本页不可用。
  2. 线程 dump 的用法:接口卡死时抓一次 dump,找 BLOCKED / 长时间 RUNNABLE 的栈,定位锁竞争或死循环。SBA 没启用时可以用 jstack <pid> 顶上。
  3. 日志级别在线调:排障期把业务包调到 DEBUG,复现后调回 INFO——无需重启,但重启后会还原。SBA 没启用时只能改配置重启。
  4. GC 曲线读法:Full GC 频繁且堆回收后仍高水位,多为内存泄漏或堆配置过小;对照「度量」页 Old 区曲线。
  5. 配置键覆盖:url.spring-boot-admin 与 url.druid 一样走配置管理,改完刷新本页即生效,无需重新发版前端。
  6. <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 即加载它——这也是不改后端就能让本页「有东西看」的唯一办法。

相关页面 ​