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端点
可以看到,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