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