likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 11

前奏:先说一下 Java VisualVM

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

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

$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 Admin 监控 Spring Boot 微服务项目

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

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

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

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

序曲: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

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

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

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

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

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

返回信息:

{
    "status": "UP"
}

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

management:
  endpoint:
    health:
      show-details: always

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

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/loggersloggers 配置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 关键字:

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

进入 DiskSpaceHealthIndicator 类:

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

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

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

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

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

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

这样一个 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: '*'

启动服务:

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

然后看监控:

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

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

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

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

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

然后再重启:

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

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

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

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

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

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

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

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