高级版的 jvisualvm :Spring Boot Admin 监控 Spring Boot 微服务项目
前奏:先说一下 Java VisualVM
Java VisualVM 是一个能够监控 JVM 的 jdk 自带的图形化工具:
在 $JAVA_HOME/bin
目录下,可直接运行它。
要想监控远程服务器上的 Java 程序,可以在启动项目的时候添加 jmx 启动参数,我提前准备了一个 spring-boot-admin-server-0.0.1-SNAPSHOT.jar
jar 包,上传到我的 Linux 服务器上,添加 jmx 启动参数启动它:
java -jar -Djava.rmi.server.hostname=192.168.242.112 -Dcom.sun.management.jmxremote.port=1888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false spring-boot-admin-server-0.0.1-SNAPSHOT.jar
现在打开 jvisualvm ,监控该程序非常简单:
连接上之后,就可以对程序进行监控了,各种指标一览无余:
单体项目,使用 jvisualvm 进行监控完全够用了!
序曲:Spring Boot Actuator 监控
Spring Boot 的 Actuator 是一个用于监控和管理自身应用信息的模块,使用该模块非常简单,只需要加入 spring-boot-starter-actuator
依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
我们在 SpringCloudAlibabaDemo
工程下创建一个子工程 spring-boot-admin-server
,引入上述依赖,然后启动项目,访问:http://localhost:8082/actuator
:
这是默认配置下,访问 Actuator 时的信息。
Actuator 端点(Endpoints)可以监视应用程序并与之交互。 Spring Boot 包含许多内置端点,并允许添加自己的端点。例如,health
端点提供基本的应用程序健康信息。
每个单独的端点都可以通过 HTTP 或 JMX 启用或禁用和远程访问。大多数应用程序选择通过 HTTP 对外暴露信息,访问端点的 URL 由 /actuator
前缀以及端点的 ID 组成。例如,默认情况下,health
端点映射到 /actuator/health
,即查看应用程序的健康状况可以访问 URL http://localhost:8082/actuator/health
:
返回信息:
{
"status": "UP"
}
UP 表示当前应用处于健康状态,如果是 DOWN 则表示不健康,增加 management.endpoints.health.show-details=ALWAYS
可以查看应用健康状况的详细信息:
management:
endpoint:
health:
show-details: always
Actuator 提供了很多端点信息,如下所示:
HTTP方法 | 端点路径 | 含义 | 是否默认暴露 |
---|---|---|---|
GET | /actuator/configprops | 显示所有 @ConfigurationProperties 的配置列表。 | false |
GET | /actuator/beans | 查看 Bean 及其关系列表 | false |
GET | /actuator/heapdump | 下载堆栈信息 | false |
GET | /actuator/env | 查看所有环境变量 | false |
GET | /actuator/health | 查看应用的健康指标 | true |
GET | /actuator/info | 查看应用信息 | false |
GET | /actuator/mappings | 查看所有 URL 映射 | false |
GET | /actuator/httptrace | 追踪信息 | false |
GET | /actuator/loggers | loggers 配置 | false |
GET | /actuator/scheduledtasks | 显示定时任务 | false |
GET | /actuator/threaddump | 显示线程信息 | false |
GET | /actuator/sessions | 允许从 Spring Session 支持的会话存储中检索和删除用户会话 | false |
完整列表见 Spring 官网:docs.spring.io/spring-boot… 。注意选择的 Spring Boot 版本信息。
从这个表可以看出,大部分的端点默认是不暴露的,我们可以通过如下配置进行合理的端点暴露(多个用逗号分开):
management:
endpoints:
web:
exposure:
include: beans,threaddump
暴露全部的话,直接进行如下配置即可:
management:
endpoints:
web:
exposure:
include: '*'
当然,我们也可以扩展 actuator 端点,拿 health
端点为例,我们先看一下其返回的结果:
{
"status": "UP",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 18238930944,
"free": 11510571008,
"threshold": 10485760,
"exists": true
}
},
"ping": {
"status": "UP"
}
}
}
可以看到,返回结果中有 components
下的 diskSpace
,我们从 spring-boot-actuator
源码中找到该返回结果的定义,在 spring-boot-actuator
中搜 diskSpace
关键字:
进入 DiskSpaceHealthIndicator 类:
可以看到该类继承 AbstractHealthIndicator 类,并在 doHealthCheck()
方法中定义了返回详情,那么我们自定义一个自己的健康检查类,输出自定义的信息的话,我们也创建一个 Component
,让他继承 AbstractHealthIndicator 实现输出自定义的信息。
/**
* 博客:https://chendapeng.cn - 行百里者半九十,凡是善始善终,吾将上下而求索!
* 公众号:行百里er
*
* @author 行百里者
* @date 2022-08-28 17:13
*/
@Component
public class ChenDapengHealthIndicator extends AbstractHealthIndicator {
/**
* 自定义健康检查输出信息
*/
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
builder.up()
.withDetail("status", "Oj8K!")
.withDetail("whoami", "外星人")
.withDetail("uptime", new Date());
}
}
再次访问健康状况,结果如下:
高潮:Spring Boot Admin
有了 Actuator 的监控信息,我们总不能每次都站在干岸上看那些无聊的 JSON 数据吧?当然不能委屈自己,目下就有一个能够将 Actuator 的信息进行图形化展示的监控管理软件,没错,他就是 Spring Boot Admin 。
更让人兴奋的是,Spring Boot Admin 还能监控注册中心中的所有微服务的情况,并且还提供实时警报功能,这个使用 jvisualvm 就没办法搞了。
Spring Boot Admin 是一个开源项目,项目地址:github.com/codecentric… 。
创建 Spring Boot Admin Server
Spring Boot Admin 分为 Server
端和 Client
端。
Server
端引入 spring-boot-admin-starter-server
的依赖:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.5.1</version>
</dependency>
并且在启动类上加 @EnableAdminServer
注解:
@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminServerApplication.class, args);
}
}
启动项目后,输入 http://localhost:8082/
访问:
这样一个 admin server
就起来了。
将微服务注册到 Spring Boot Admin
Spring Boot Admin 怎么去监控服务呢,得有服务注册到 Admin Server 上来才行,所以我们将之前的几个微服务注册进来。
需要注册到 Admin Server 上的服务我们暂且称之为客户端,那么客户端需要怎么做呢?
1, 每个需要注册到 Admin Server 的服务都引入如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.5.1</version>
</dependency>
2, 配置连接到 Admin Server 的 url :
spring:
boot:
admin:
client:
url: http://localhost:8082
3, 配置对外暴露所有 Actuator 端点:
management:
endpoints:
web:
exposure:
include: '*'
启动服务:
然后看监控:
what!竟然全部灰色,意思是全部离线,可是我的所有服务都正常启动了啊,点进去看一下:
看这个提示,应该能明白的差不多了,应该需要我们把应用的 url 在 admin 中显示为 ip 地址应该就可以了,果然有这个配置:
spring:
boot:
admin:
client:
url: http://localhost:8082
# 注册时 admin 中显示IP地址不显示主机名
prefer-ip: true
然后再重启:
舒服了,全部在线!找一个服务点进去看细节:
这个图是不是似曾相识啊!
没错,这个如就是和前面我们提到的 jvisualvm 的监控界面差不多,都有对 进程
、线程
、垃圾回收
、内存
等的监控,还有 JVM
各种指标的监控,想要康的东西应有尽有!
开启 Spring Boot Admin 认证
Spring Boot Admin 服务很有可能需要在外网访问,不可能让所有人输入一个网址就能随随便便看到我们服务的信息,因此需要登录功能。
Spring Boot Admin 官方说,由于有多种方法可以解决分布式 Web 应用程序中的身份验证和授权,因此 Spring Boot Admin 没有提供默认方法。我们借助于 spring-boot-starter-security
就可以实现开启登录认证功能。
1, 引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2, 在 application.yml
配置文件中配置:
spring:
security:
user:
name: admin
password: admin123
3, 自定义安全配置类:
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
private final AdminServerProperties adminServer;
private final SecurityProperties security;
public MySecurityConfig(AdminServerProperties adminServer, SecurityProperties security) {
this.adminServer = adminServer;
this.security = security;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeRequests(
(authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**")).permitAll()
.antMatchers(this.adminServer.path("/actuator/info")).permitAll()
.antMatchers(this.adminServer.path("/actuator/health")).permitAll()
.antMatchers(this.adminServer.path("/login")).permitAll().anyRequest().authenticated()
).formLogin(
(formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout"))).httpBasic(Customizer.withDefaults())
.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
new AntPathRequestMatcher(this.adminServer.path("/instances"),
HttpMethod.POST.toString()),
new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
HttpMethod.DELETE.toString()),
new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
))
.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
}
// Required to provide UserDetailsService for "remember functionality"
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(security.getUser().getName())
.password("{noop}" + security.getUser().getPassword()).roles("USER");
}
}
该配置类是 Spring Boot Admin 官网提供的:codecentric.github.io/spring-boot…
再次访问 admin 的时候,就需要登录了:
最后,需要注意的是,既然 admin server 开启了登录认证,那么向 server 中注册的服务也必须得提供用户名密码才能注册上去,在客户端添加如下配置即可:
spring.boot.admin.client:
username: admin
password: admin123
集成 Nacos 进行服务发现
不知道大家发现了没有,我们要想让 Admin Server 监控我们的微服务,所有的微服务都得引入 admin 的相关依赖并且都得配置 admin server 的地址,用户名,密码等。这样太繁琐了!
其实我们可以将 admin server 也注册到 Nacos 注册中心,然后 admin server 自动从 Nacos 中获取服务信息来统一查看。
在 admin server 中引入 nacos 依赖,以及配置注册中心地址:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
sping:
cloud:
nacos:
discovery:
# Nacos 集群
server-addr: 192.168.242.112:81
这样就不用每个客户端都要写一大堆的配置了!
小结:为啥要监控?
因为面试官可能会问:“你平时是怎么调优的?” 呵呵,当然不是。
不过我们平时工作中,真遇到 OOM问题排查 、项目参数调优 等问题的时候,或许可以这么做:
- 如果是单一项目,通过 JDK 自带的 jvisualvm 监控工具对项目进行监控,通过查看到具体装了多少个类进来,包括多少个线程以及线程运行的时间,通过图形化界面和容易就能找到是哪个类占用的内存比较多,然后再定位具体的问题。
- 如果是多个项目我们可以通过 Spring Boot Admin 这类工具进行微服务项目监控,可以看到注册到注册中心(比如 Nacos)的所有服务的状况,然后再具体问题具体分析。
为了排查和解决问题,为了优化程序,我们需要监控。
转载自:https://juejin.cn/post/7137095477526265863