springboot04_springboot特性之Actuator

4.1.8.springboot特性之Actuator【上】

时长:23min

4.3.Actuator是干啥的?

  它也是以starter组件方式,进行引入的。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

当添加这个组件之后,再去启动当前应用,就会看到当前项目的健康状态,一些相关信息。

是通过Endpoints窗口进行信息展示,如下所示:

 4.3.1.通过官方文档分析actuator有哪些endpoints端点

https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints

 可以看到,springboot-actuator中提供了很多的端点,如:health,beans,mappings,...

http://localhost:8080/actuator/info,本地启动后,访问端点,默认情况,只放开info,health,其它访问

会提示错误。

  我们可以通过配置,访问更多端点,配置如下:

management.endpoints.web.exposure.include=*

再次运行项目,就可以访问更多端点了。如:env,beans,...

http://localhost:8080/actuator/env

4.3.1.1.查看health端点详情

http://localhost:8080/actuator/health

  设置多端点可访问之后,启动springboot项目,查看到的结果是:

 只能看到status值。如果想要看到更详细的信息,应该怎么办呢?

配置如下的属性:

该配置,默认值为never,现在修改为always.然后重启项目,访问health,结果如下:

 这时,展示的健康状态,不仅包含应用本身的状态,还包含全局配置中其它连接,如:redis连接的状态。

注意:

  我们如果自己定义一个redis连接组件,然后进行配置是不会监控它的状态的。它只监控spring默认支持配置

的健康状态。

  下面来看下redis监控代码实现:

 通过继承AbstractReactiveHealthIndicator抽象类,并对连接进行监控,up表示连接成功,down表示连接失败。

4.3.1.2.查看metrics端点详情

http://localhost:8080/actuator/metrics

监控对象:

  》jvm【垃圾收集器、内存、堆】

  》系统层面【运行时间,平均负载。。】

  》线程池的状态

  》容器【如tomcat】状态

1.具体key的详细信息查看

  以"process.start.time"为例。只需要访问:

http://localhost:8080/actuator/metrics/process.start.time

结果如下所示:

 

4.1.9.springboot特性之Actuator【下】

时长:1h6min

metrics,主要是系统运行的状态值进行监控。

  》pheuthous/grafana【图标展示】

4.3.1.3.loggers监控

  http://localhost:8080/actuator/loggers

  主要用来展示不同包路径设置的日志级别,它的作用有:

如果在线上想去排查问题,要查看日志的详细信息,提供一种方式可以修改,可以

对特定节点的日志级别进行修改。

  比如说,要修改ROOT节点的日志级别。默认级别是info,现在准备修改为debug.

1.日志级别修改

修改方法如下:

使用postman发起一个POST请求:http://localhost:8080/actuator/loggers/ROOT,并在

body中传参json数据:{"configuredLevel": "DEBUG"},如下所示:

然后,查看日志级别,已经修改为DEBUB,如下所示:

再看项目后台输出日志级别,也变成为DEBUG,如下所示:

 同理,也可以改回INFO级别。

4.3.1.4.查看项目的一些info信息

  需要进行配置,如:info.app.name = @project.name@

  然后,重启项目。访问info,即http://localhost:8080/actuator/info,如下所示:

 4.3.2自已开发endpoint端点

4.3.2.1.注解方式定义一个Indicator

代码如下所示:

package com.wf.demo.springbootdemo.endpoint;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName CustomerMetricsIndicator
 * @Description 自定义,用户状态监控器
 * @Author wf
 * @Date 2020/7/9 10:23
 * @Version 1.0
 */
@Endpoint(id = "customer")
public class CustomerMetricsIndicator {
    @ReadOperation
    public Map<String,Object> time(){
        Map<String,Object> map = new HashMap<String, Object>();
        Date time = new Date();
        map.put("当前时间",time);
        return map;
    }
}
4.3.2.2.定义endpoint配置类注入bean
package com.wf.demo.springbootdemo.endpoint;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName EndpointConfiguration
 * @Description 注入endpoint bean的配置类
 * @Author wf
 * @Date 2020/7/9 10:30
 * @Version 1.0
 */
@Configuration
public class EndpointConfiguration {
    @Bean
    public CustomerMetricsIndicator customerMetricsIndicator(){
        return new CustomerMetricsIndicator();
    }
}

然后,重启项目, 访问端点:http://localhost:8080/actuator/customer,结果如下:

 说明: 

  自定义endpoint就成功完成了。

 4.4.actuator的两种监控状态

分别是:

》http【web】

》jmx

4.4.1.JMX

所谓JMX,是Java Management Extensions(Java管理扩展)的缩写,是一个为应用程序植入管理功能的框架。

用户可以在任何Java应用程序中使用这些代理和服务实现管理。

  使用jmx可以做一些监控。

4.4.1.1.springboot项目jmx监控使用
1.需要配置jmx使能
management.endpoints.jmx.exposure.include=*
spring.jmx.enabled=true

然后重启项目,使用jdk提供jconsole工具连接jmx监控端点。

2.jconsole连接
》jconsle打开

 window下在命令搜索框【win+R打开】中,输入jconsole,按下enter,打开图形界面,如下所示:

 

 》连接某个java进程

  选择本地启动项目,所在java进程,然后点击,连接,如下所示:

如果出现如下所示,连接报错。点击不安全连接,即可。

 连接成功后,进入如下界面:

3.查看endpoint信息 

在MBean下可以查看到,相关的endpoint信息,如下所示:

 这些监控信息有什么用呢?应该如何应用它?

  我们在做监控系统时,会涉及到当前应用的服务信息上报,总不能从监控平台手动去抓取吧!

  因为监控平台要做这样一些信息抓取功能,在设计上是很困难的。比如,要抓取哪些信息,是否有用,

还要做数据清洗等,很麻烦。

  一般来说,监控信息更多的是上报,在程序中加埋点,加监控的api,通过report去上报。java里面要把这

些信息上报的话,就可以使用jmx这样来做。

 4.4.1.2.通过示例代码来说明jmx的作用
1.开发一个接口
package com.wf.demo.springbootdemo;

/**
 * @ClassName SystemInfoMBean
 * @Description 系统信息接口
 * @Author wf
 * @Date 2020/7/9 11:07
 * @Version 1.0
 */
public interface SystemInfoMBean {
    //获得当前cpu核心数
    int getCpuCore();
    //内存是多大
    long getTotalMemory();

    //关闭
    void shutdown();
}

说明:

  定义一个接口,暴露一些要监控的信息,接口命名格式必须固定,如:*MBean【后缀固定】

2.定义接口实现类
package com.wf.demo.springbootdemo;

/**
 * @ClassName SystemInfo
 * @Description 实现类,命名和接口严格相关
 * @Author wf
 * @Date 2020/7/9 11:12
 * @Version 1.0
 */
public class SystemInfo implements SystemInfoMBean {
    @Override
    public int getCpuCore() {
        return Runtime.getRuntime().availableProcessors();
    }

    @Override
    public long getTotalMemory() {
        return Runtime.getRuntime().totalMemory();
    }

    @Override
    public void shutdown() {
        System.exit(0);
    }
}

注意:

  实现类命名,必须遵照规范。即接口名去掉MBean后缀。

3.发布信息
package com.wf.demo.springbootdemo;

import javax.management.*;
import java.io.IOException;
import java.lang.management.ManagementFactory;

/**
 * @ClassName JMXMain
 * @Description 发布信息
 * @Author wf
 * @Date 2020/7/9 11:32
 * @Version 1.0
 */
public class JMXMain {
    public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, IOException {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ObjectName objectName = new ObjectName("com.wf.demo.springbootdemo:type=SystemInfo");
        //把信息发布出去
        SystemInfo systemInfo = new SystemInfo();
        mBeanServer.registerMBean(systemInfo,objectName);

        //避免main方法结束,阻塞在这里
        System.in.read();
    }
}

然后,运行这个main程序。然后重新打开jconsole查看,是否有监控到自定义内容。

 

 可以看到,jmx监控就完成了。

  在这个监控信息中,有属性和操作两个类别,它是如何归属的呢?

  它和接口定义的方法有关系,如:是getXX方法,就归类为属性,否则归类为操作。

思考:

  现在,我们是通过main方法进行发布监控信息的。那么,springboot是如何做到这个发布功能的呢?

  在前面我们定义的Indicator类,使用了@Endpoint注解,springboot通过扫描这个注解,并自动通过

web和jmx两种形式的发布。

  

  下面举例说明,springboot是如何发布信息的。

在springApplication中有属性和操作两种分类,它们分别是怎么注册的?

  如果是jmx发布,必然是有一个MBeanServer去完成一个发布。通过搜索springboot的代码,有

SpringApplicationAdminMXBean接口,定义bean信息。如下所示:

 

 根据JMX的规范,上面的接口必然有一相关的实现类SpringApplicationAdmin,但是,这里是一个内部类

如下所示:

 是在SpringApplicationAdminMXBeanRegistrar中定义的内部类,这个类是提供注册功能的类。

它的注入,是通过配置类自动装配的,配置类为SpringApplicationAdminJmxAutoConfiguration

 

 4.4.1.3.SpringApplicationAdminMXBeanRegistrar如何实现jmx发布功能原理分析

  该bean实现多个接口功能,如下所示:

public class SpringApplicationAdminMXBeanRegistrar implements ApplicationContextAware, GenericApplicationListener, EnvironmentAware, InitializingBean, DisposableBean {
    private static final Log logger = LogFactory.getLog(SpringApplicationAdminMXBeanRegistrar.SpringApplicationAdmin.class);
    private ConfigurableApplicationContext applicationContext;
    private Environment environment = new StandardEnvironment();
    private final ObjectName objectName;
    private boolean ready = false;
    private boolean embeddedWebApplication = false;
1.ApplicationContextAware接口

  获得读取上下文能力。在Spring容器中一个bean如果实现了该方法则就可以获取上下文对象。

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext var1) throws BeansException;
}

说明:

  该接口继承自Aware,定义setApplicationContext,通过set方法,实现bean注入springIOC容器

  实现类中处理,就是一个set注入,如下:

 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Assert.state(applicationContext instanceof ConfigurableApplicationContext, "ApplicationContext does not implement ConfigurableApplicationContext");
        this.applicationContext = (ConfigurableApplicationContext)applicationContext;  //set注入
    }
2. 实现GenericApplicationListener接口

  获取处理事件的能力,同样在Spring中只要实现该接口,就获取了事件监听的能力,

不过具体监听什么事件要自己去判断。大家可以根据例子来理解。接口定义 如下:

public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(ResolvableType var1);

    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }

    default int getOrder() {
        return 2147483647;
    }
}
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
 

下面看下子类实现:

public boolean supportsEventType(ResolvableType eventType) {
        Class<?> type = eventType.getRawClass();
        if (type == null) {
            return false;
        } else {  //根据事件泛型类型,判断是否进行事件处理,ApplicationReadyEvent或WebServerInitializedEvent类型就处理
            return ApplicationReadyEvent.class.isAssignableFrom(type) || WebServerInitializedEvent.class.isAssignableFrom(type);
        }
    }

    public boolean supportsSourceType(Class<?> sourceType) {
        return true;
    }

    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            this.onApplicationReadyEvent((ApplicationReadyEvent)event);
        }

        if (event instanceof WebServerInitializedEvent) {
            this.onWebServerInitializedEvent((WebServerInitializedEvent)event);
        }

    }

    public int getOrder() {
        return -2147483648;
    }
  //spring已经准备好了,设置ready=true
    void onApplicationReadyEvent(ApplicationReadyEvent event) {
        if (this.applicationContext.equals(event.getApplicationContext())) {
            this.ready = true;
        }

    }
  //web应用,这里处理
    void onWebServerInitializedEvent(WebServerInitializedEvent event) {
        if (this.applicationContext.equals(event.getApplicationContext())) {
            this.embeddedWebApplication = true;
        }

    }
3. 实现EnvironmentAware接口

  获取应用配置环境信息, 和上面一样实现了Aware结尾的接口,都能获取对应的Spring内容的对象实例,

然后我们就可以根据该实例,来进行功能扩展。接口定义如下:

public interface EnvironmentAware extends Aware {
    void setEnvironment(Environment var1);
}

  实现类中处理逻辑:

public void setEnvironment(Environment environment) {
        this.environment = environment;  //set注入
    }
4. 实现InitializingBean接口

  这里就要着重看了,在初始化时候将MBean注册到JMX上。

当然我们可以通过 @PostConstruct注解来声明初始化方法。接口定义如下:

package org.springframework.beans.factory;

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

  子类实现逻辑:

public void afterPropertiesSet() throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();  //用来发布JMX,SpringApplicationAdmin是要发布接口实现类
        server.registerMBean(new SpringApplicationAdminMXBeanRegistrar.SpringApplicationAdmin(), this.objectName);
        if (logger.isDebugEnabled()) {
            logger.debug("Application Admin MBean registered with name '" + this.objectName + "'");
        }

    }

注意:

  this.objectName是如何设置的?

  private final ObjectName objectName;  //通过构造器传参,是从哪里从过来的呢?
    private boolean ready = false;
    private boolean embeddedWebApplication = false;

    public SpringApplicationAdminMXBeanRegistrar(String name) throws MalformedObjectNameException {
        this.objectName = new ObjectName(name);
    }

 构造器是在配置类中自动装配时,设置的,如下所示:

public class SpringApplicationAdminJmxAutoConfiguration {
    private static final String JMX_NAME_PROPERTY = "spring.application.admin.jmx-name";
    private static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";//固定格式

    public SpringApplicationAdminJmxAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar(ObjectProvider<MBeanExporter> mbeanExporters, Environment environment) throws MalformedObjectNameException {
        String jmxName = environment.getProperty("spring.application.admin.jmx-name", "org.springframework.boot:type=Admin,name=SpringApplication");
        if (mbeanExporters != null) {
            Iterator var4 = mbeanExporters.iterator();

            while(var4.hasNext()) {
                MBeanExporter mbeanExporter = (MBeanExporter)var4.next();
                mbeanExporter.addExcludedBean(jmxName);
            }
        }

        return new SpringApplicationAdminMXBeanRegistrar(jmxName);  //这里设置jmx监控的包路径及类名
    }
}
5. 实现DisposableBean接口

  应用销毁时候,取消注册。同样我们也可以用@PreDestroy注解来实现。

  public void destroy() throws Exception {
        ManagementFactory.getPlatformMBeanServer().unregisterMBean(this.objectName);//取消注册
    }

  如果我们自己想要去注入这样一个注册bean,可以使用@Component进行注入。

 4.5.大盘展示系统应用监控信息

4.5.1.springboot监控信息可发布到prometheus + grafana

4.5.1.1.安装prometheus 组件

  它是一个开源监控系统。它有几个核心模块组成:

》数据爬虫,可以根据配置实现定期抓取数据,基于http抓取一些metrics数据,即进行数据采集

》时序数据库存储,存储metrics数据

》可视化,但它的可视化并不好用,所以,才结合grafana组件来可视化展示。

1.如何安装

  去github上搜索,下载安装包,选择prometheus-2.19.2.linux-amd64.tar.gz版本进行下载。

https://github.com/prometheus/prometheus/releases/tag/v2.19.2

  如果是使用xshell连接服务器,使用wget进行下载,可复制出安装包路径。

https://github.com/prometheus/prometheus/releases/download/v2.19.2/prometheus-2.19.2.linux-amd64.tar.gz

[root@localhost ~]# wget https://github.com/prometheus/prometheus/releases/download/v2.19.2/prometheus-2.19.2.linux-amd64.tar.gz
-bash: wget: command not found  //没有wget命令,先安装
[root@localhost ~]# yum install -y wget

由于是从github地址进行下载,虽然只有61M大小,速度还是比较慢。

wget -c https://github.com/prometheus/prometheus/releases/download/v2.19.2/prometheus-2.19.2.linux-amd64.tar.gz  //支持断点续传

  

2.springboot项目集成prometheus组件

pom.xml添加依赖包:

 <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>//版本由springboot管理

 然后,重启springboot项目,访问:http://localhost:8080/actuator/prometheus

原文地址:https://www.cnblogs.com/wfdespace/p/13268598.html