高级版的 jvisualvm :Spring Boot Admin 监控 Spring Boot 微服务项目

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情

前奏:先说一下 Java VisualVM

Java VisualVM 是一个能够监控 JVM 的 jdk 自带的图形化工具:

image-20220828164318671.png

$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 ,监控该程序非常简单:

image-20220828165434650.png

连接上之后,就可以对程序进行监控了,各种指标一览无余:

image-20220828165532244.png

image-20220828165556502.png

单体项目,使用 jvisualvm 进行监控完全够用了!

扫描二维码关注公众号,回复: 14503895 查看本文章

序曲:Spring Boot Actuator 监控

Spring BootActuator 是一个用于监控和管理自身应用信息的模块,使用该模块非常简单,只需要加入 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

image-20220828154513357.png

这是默认配置下,访问 Actuator 时的信息。

Actuator 端点(Endpoints)可以监视应用程序并与之交互。 Spring Boot 包含许多内置端点,并允许添加自己的端点。例如,health 端点提供基本的应用程序健康信息。

每个单独的端点都可以通过 HTTP 或 JMX 启用或禁用和远程访问。大多数应用程序选择通过 HTTP 对外暴露信息,访问端点的 URL 由 /actuator 前缀以及端点的 ID 组成。例如,默认情况下,health 端点映射到 /actuator/health ,即查看应用程序的健康状况可以访问 URL http://localhost:8082/actuator/health

image-20220828155556190.png

返回信息:

{
    "status": "UP"
}
复制代码

UP 表示当前应用处于健康状态,如果是 DOWN 则表示不健康,增加 management.endpoints.health.show-details=ALWAYS 可以查看应用健康状况的详细信息:

management:
  endpoint:
    health:
      show-details: always
复制代码

image-20220828162539633.png

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 关键字:

image-20220828170644769.png

进入 DiskSpaceHealthIndicator 类:

image-20220828171018416.png

可以看到该类继承 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());
    }
}

复制代码

再次访问健康状况,结果如下:

image-20220828171936525.png

高潮: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/ 访问:

image-20220828174535440.png

这样一个 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: '*'
复制代码

启动服务:

image-20220828183431176.png

然后看监控:

image-20220828183509057.png

what!竟然全部灰色,意思是全部离线,可是我的所有服务都正常启动了啊,点进去看一下:

image-20220828184338625.png

看这个提示,应该能明白的差不多了,应该需要我们把应用的 url 在 admin 中显示为 ip 地址应该就可以了,果然有这个配置:

spring:
  boot:
    admin:
      client:
        url: http://localhost:8082
        # 注册时 admin 中显示IP地址不显示主机名
        prefer-ip: true
复制代码

然后再重启:

image-20220828184926914.png

舒服了,全部在线!找一个服务点进去看细节:

image-20220828185025897.png

这个图是不是似曾相识啊!

没错,这个如就是和前面我们提到的 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 的时候,就需要登录了:

image-20220828191056379.png

最后,需要注意的是,既然 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)的所有服务的状况,然后再具体问题具体分析。

为了排查和解决问题,为了优化程序,我们需要监控。

猜你喜欢

转载自juejin.im/post/7137095477526265863