Springboot Actuator之十一:actuator PublicMetrics

前言
接下來的幾篇文章咱們來分析一下spring-boot-actuator 中在org.springframework.boot.actuate.metrics中的代碼,如圖:java

 

這裏的代碼不只多,並且還比較複雜(類與類之間的關聯關係).咱們的策略是一點一點的蠶食,本文就先來分析PublicMetrics的實現,關於這部分的類圖以下:web

 

本文只分析PublicMetrics, SystemPublicMetrics, TomcatPublicMetrics, DataSourcePublicMetrics.其餘的實現–>CachePublicMetrics,MetricReaderPublicMetrics,RichGaugeReaderPublicMetrics 咱們後續的文章進行講解.spring

解析
PublicMetrics
PublicMetrics,暴露指定的Metric經過MetricsEndpoint的接口.實現類應該當心metrics–> 它們提供了一個惟一的名字在application context中,可是它們不能在jvm,分佈式環境時惟一的數據庫

該類只聲明瞭1個方法,代碼以下:tomcat

// 返回表示當前的狀態的indication 經過metrics
Collection<Metric<?>> metrics();
1
2
這裏有必要說明一下Metric,該類是1個持有系統測量值的不變類(1個被命名的數值和事件戳的類) 好比:測量1個服務器的活躍連接數或者是測量會議室的溫度.該類是1個泛型類,其泛型參數T–>測量值的類型.其字段,構造器以下:服務器

private final String name;session

private final T value;app

private final Date timestamp;jvm

1
2
3
4
5
6
7
SystemPublicMetrics
SystemPublicMetrics–>提供各類與系統相關的度量的PublicMetrics實現,該類實現了PublicMetrics,Ordered.實現Ordered的接口的目的是在有多個PublicMetrics的集合中進行排序.,其實現以下:分佈式

public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 10;
}
1
2
3
字段,構造器以下:

// 啓動時間(指的是SystemPublicMetrics初始化的時間)
private long timestamp;

public SystemPublicMetrics() {
this.timestamp = System.currentTimeMillis();
}
1
2
3
4
5
6
metrics,實現以下:

public Collection<Metric<?>> metrics() {
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>();
// 1. 添加基本的統計
addBasicMetrics(result);
// 2. 添加uptime,負載等統計
addManagementMetrics(result);
return result;
}
1
2
3
4
5
6
7
8
添加基本的統計.代碼以下:

protected void addBasicMetrics(Collection<Metric<?>> result) {
// NOTE: ManagementFactory must not be used here since it fails on GAE
Runtime runtime = Runtime.getRuntime();
// 1. 添加內存使用統計,name=mem,value = 總內存+堆外內存使用量
result.add(newMemoryMetric("mem",
runtime.totalMemory() + getTotalNonHeapMemoryIfPossible()));
// 2. 添加可用統計,name=mem.free,value = 可用內存
result.add(newMemoryMetric("mem.free", runtime.freeMemory()));
// 3. 添加處理器核心數統計,name=processors,value = 處理器核心數
result.add(new Metric<Integer>("processors", runtime.availableProcessors()));
// 4. 添加SystemPublicMetrics 運行時間統計,name=instance.uptime,value = 當前時間-啓動時間
result.add(new Metric<Long>("instance.uptime",
System.currentTimeMillis() - this.timestamp));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
添加內存使用統計,name=mem,value = 總內存+堆外內存使用量,其中堆外內存使用量代碼以下:

private long getTotalNonHeapMemoryIfPossible() {
try {
return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed();
}
catch (Throwable ex) {
return 0;
}
}
1
2
3
4
5
6
7
8
添加可用統計,name=mem.free,value = 可用內存
添加處理器核心數統計,name=processors,value = 處理器核心數
添加SystemPublicMetrics 運行時間統計,name=instance.uptime,value = 當前時間-啓動時間
添加uptime,負載等統計.代碼以下:

private void addManagementMetrics(Collection<Metric<?>> result) {
try {
// Add JVM up time in ms
// 1. 添加jvm啓動時間,name=uptime,value = 啓動時間,單位ms
result.add(new Metric<Long>("uptime",
ManagementFactory.getRuntimeMXBean().getUptime()));
// 2. 添加系統負載,name=systemload.average,value = 啓動負載
result.add(new Metric<Double>("systemload.average",
ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()));
// 3. 添加jvm的監控統計
addHeapMetrics(result);
// 4. 添加堆外內存的統計
addNonHeapMetrics(result);
// 5. 添加線程的統計
addThreadMetrics(result);
// 6. 添加類加載相關的統計
addClassLoadingMetrics(result);
// 7. 添加垃圾回收的統計
addGarbageCollectionMetrics(result);
}
catch (NoClassDefFoundError ex) {
// Expected on Google App Engine
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
添加jvm啓動時間,name=uptime,value = 啓動時間,單位ms
添加系統負載,name=systemload.average,value = 啓動負載
添加jvm的監控統計,代碼以下:

protected void addHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();
// 1. 得到所提交的字節內存量-->這個內存量是保證java虛擬機使用的
result.add(newMemoryMetric("heap.committed", memoryUsage.getCommitted()));
// 2. 得到jvm的初始化內存數,單位:字節.若是初始內存大小未定義,則此方法返回-1
result.add(newMemoryMetric("heap.init", memoryUsage.getInit()));
// 3. 得到內存的使用量
result.add(newMemoryMetric("heap.used", memoryUsage.getUsed()));
// 4. 得到內存的最大值,返回-1,若是爲指定
result.add(newMemoryMetric("heap", memoryUsage.getMax()));
}
1
2
3
4
5
6
7
8
9
10
11
12
得到所提交的字節內存量–>這個內存量是保證java虛擬機使用的
得到jvm的初始化內存數,單位:字節.若是初始內存大小未定義,則此方法返回-1
得到內存的使用量
得到內存的最大值,返回-1,若是未指定
添加堆外內存的統計,代碼以下:

private void addNonHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getNonHeapMemoryUsage();
result.add(newMemoryMetric("nonheap.committed", memoryUsage.getCommitted()));
result.add(newMemoryMetric("nonheap.init", memoryUsage.getInit()));
result.add(newMemoryMetric("nonheap.used", memoryUsage.getUsed()));
result.add(newMemoryMetric("nonheap", memoryUsage.getMax()));
}
1
2
3
4
5
6
7
8
得到所提交的字節內存量–>這個內存量是保證java虛擬機使用的
得到jvm的初始化內存數,單位:字節.若是初始內存大小未定義,則此方法返回-1
得到內存的使用量
得到內存的最大值,返回-1,若是未指定
添加線程的統計,代碼以下:

protected void addThreadMetrics(Collection<Metric<?>> result) {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
// 1. 得到jvm啓動以來或者統計重置以來的最大值
result.add(new Metric<Long>("threads.peak",
(long) threadMxBean.getPeakThreadCount()));
// 2. 得到daemon線程的數量
result.add(new Metric<Long>("threads.daemon",
(long) threadMxBean.getDaemonThreadCount()));
// 3. 得到jvm啓動以來被建立而且啓動的線程數
result.add(new Metric<Long>("threads.totalStarted",
threadMxBean.getTotalStartedThreadCount()));
// 4. 得到當前存活的線程數包括daemon,非daemon的
result.add(new Metric<Long>("threads", (long) threadMxBean.getThreadCount()));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
得到jvm啓動以來或者統計重置以來的最大值
得到daemon線程的數量
得到jvm啓動以來被建立而且啓動的線程數
得到當前存活的線程數包括daemon,非daemon的
添加類加載相關的統計,代碼以下:

protected void addClassLoadingMetrics(Collection<Metric<?>> result) {
ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
// 1. 得到jvm目前加載的class數量
result.add(new Metric<Long>("classes",
(long) classLoadingMxBean.getLoadedClassCount()));
// 2.得到jvm啓動以來加載class的全部數量
result.add(new Metric<Long>("classes.loaded",
classLoadingMxBean.getTotalLoadedClassCount()));
// 3. 得到jvm卸載class的數量
result.add(new Metric<Long>("classes.unloaded",
classLoadingMxBean.getUnloadedClassCount()));
}
1
2
3
4
5
6
7
8
9
10
11
12
得到jvm目前加載的class數量
得到jvm啓動以來加載class的全部數量
得到jvm卸載class的數量
添加垃圾回收的統計,代碼以下:

protected void addGarbageCollectionMetrics(Collection<Metric<?>> result) {
// 1. 得到GarbageCollectorMXBean
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory
.getGarbageCollectorMXBeans();
// 2.遍歷之:
for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
String name = beautifyGcName(garbageCollectorMXBean.getName());
// 2.1. 得到gc的次數
result.add(new Metric<Long>("gc." + name + ".count",
garbageCollectorMXBean.getCollectionCount()));
// 2.2. 得到gc的時間
result.add(new Metric<Long>("gc." + name + ".time",
garbageCollectorMXBean.getCollectionTime()));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
得到GarbageCollectorMXBean
遍歷之:

得到gc的次數
得到gc的時間
自動裝配:

在PublicMetricsAutoConfiguration中進行自動裝配.代碼以下:

@Bean
public SystemPublicMetrics systemPublicMetrics() {
return new SystemPublicMetrics();
}
1
2
3
4
@Bean–> 註冊1個id爲systemPublicMetrics,類型爲SystemPublicMetrics的bean
TomcatPublicMetrics
TomcatPublicMetrics–>提供tomcat的數據統計的PublicMetrics的實現.該類實現了PublicMetrics, ApplicationContextAware接口.

metrics 代碼以下:

public Collection<Metric<?>> metrics() {
// 1. 若是applicationContext 是EmbeddedWebApplicationContext的實例,則進行後續處理,不然,返回空集合
if (this.applicationContext instanceof EmbeddedWebApplicationContext) {
// 2. 得到Manager
Manager manager = getManager(
(EmbeddedWebApplicationContext) this.applicationContext);
if (manager != null) {
// 3. 若是Manager 不等於null,則調用metrics 進行收集統計數據
return metrics(manager);
}
}
return Collections.emptySet();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
若是applicationContext 是EmbeddedWebApplicationContext的實例,則進行後續處理,不然,返回空集合
得到Manager,代碼以下:

private Manager getManager(EmbeddedWebApplicationContext applicationContext) {
// 1. 得到內嵌tomcat的實例,若是不是TomcatEmbeddedServletContainer的實例,則返回null
EmbeddedServletContainer embeddedServletContainer = applicationContext
.getEmbeddedServletContainer();
if (embeddedServletContainer instanceof TomcatEmbeddedServletContainer) {
// 2. 不然,得到tomcat中Context所對應的Manager
return getManager((TomcatEmbeddedServletContainer) embeddedServletContainer);
}
return null;
}
1
2
3
4
5
6
7
8
9
10
得到內嵌tomcat的實例,若是不是TomcatEmbeddedServletContainer的實例,則返回null
不然,得到tomcat中Context所對應的Manager,代碼以下:

private Manager getManager(TomcatEmbeddedServletContainer servletContainer) {
for (Container container : servletContainer.getTomcat().getHost()
.findChildren()) {
if (container instanceof Context) {
return ((Context) container).getManager();
}
}
return null;
}
1
2
3
4
5
6
7
8
9
經過遍歷host中的Container,若是其是Context的子類,則直接得到其對應的Manager,不然,返回null.

若是Manager 不等於null,則調用metrics 進行收集統計數據.代碼以下:

private Collection<Metric<?>> metrics(Manager manager) {
List<Metric<?>> metrics = new ArrayList<Metric<?>>(2);
// 1. 若是Manager 是ManagerBase的實例,則添加 tomcat的session最大數量,-1 -->沒有限制
if (manager instanceof ManagerBase) {
addMetric(metrics, "httpsessions.max",
((ManagerBase) manager).getMaxActiveSessions());
}
// 2. 添加當前激活的session數量
addMetric(metrics, "httpsessions.active", manager.getActiveSessions());
return metrics;
}
1
2
3
4
5
6
7
8
9
10
11
12
若是Manager 是ManagerBase的實例,則添加 tomcat的session最大數量,-1 –>沒有限制
添加當前激活的session數量
addMetric 實現以下:

private void addMetric(List<Metric<?>> metrics, String name, Integer value) {
metrics.add(new Metric<Integer>(name, value));
}
1
2
3
自動裝配:

聲明在TomcatMetricsConfiguration中,代碼以下:

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
static class TomcatMetricsConfiguration {

@Bean
@ConditionalOnMissingBean
public TomcatPublicMetrics tomcatPublicMetrics() {
return new TomcatPublicMetrics();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
因爲TomcatMetricsConfiguration上聲明瞭以下註解:

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
1
2
3
所以,知足以下條件時該配置生效

@ConditionalOnClass({ Servlet.class, Tomcat.class })–> 在當前類路徑下存在Servlet.class, Tomcat.class時生效
@ConditionalOnWebApplication–> 在web環境下生效
因爲tomcatPublicMetrics方法聲明瞭 @ConditionalOnMissingBean,所以當beanFactory中不存在TomcatPublicMetrics類型的bean時生效.

DataSourcePublicMetrics
DataSourcePublicMetrics–>提供數據源使用方面的數據統計的PublicMetrics的實現.

該類的字段,以下:

private static final String DATASOURCE_SUFFIX = "dataSource";

@Autowired
private ApplicationContext applicationContext;

@Autowired
private Collection<DataSourcePoolMetadataProvider> providers;

// key---> 對DataSourcePoolMetadataProvider的id生成的前綴,value-->對應的DataSourcePoolMetadata
private final Map<String, DataSourcePoolMetadata> metadataByPrefix = new HashMap<String, DataSourcePoolMetadata>();
1
2
3
4
5
6
7
8
9
10
因爲該類的initialize方法註解有@PostConstruct,所以會在初始化後執行.代碼以下:

@PostConstruct
public void initialize() {
// 1. 嘗試獲取主數據源 返回null,意味着主數據源不存在
DataSource primaryDataSource = getPrimaryDataSource();
// 2. 實例化DataSourcePoolMetadataProvider
DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders(
this.providers);
// 3. 得到BeanFactory中DataSource類型的bean,遍歷之
for (Map.Entry<String, DataSource> entry : this.applicationContext
.getBeansOfType(DataSource.class).entrySet()) {
String beanName = entry.getKey();
DataSource bean = entry.getValue();
// 3.1 生成前綴
String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
// 3.2 得到DataSource 所對應的DataSourcePoolMetadata,放入metadataByPrefix 中
DataSourcePoolMetadata poolMetadata = provider
.getDataSourcePoolMetadata(bean);
if (poolMetadata != null) {
this.metadataByPrefix.put(prefix, poolMetadata);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
嘗試獲取主數據源 返回null,意味着主數據源不存在.代碼以下:

private DataSource getPrimaryDataSource() {
try {
return this.applicationContext.getBean(DataSource.class);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
1
2
3
4
5
6
7
8
實例化DataSourcePoolMetadataProvider
得到BeanFactory中DataSource類型的bean,遍歷之

生成前綴.代碼以下:

protected String createPrefix(String name, DataSource dataSource, boolean primary) {
// 1. 若是是主數據源,返回datasource.primary
if (primary) {
return "datasource.primary";
}
// 2. 若是DataSource對應的id 長度大於dataSource的長度,而且是dataSource結尾的,則截取以前的做爲id,如:demoDataSource--> demo
if (name.length() > DATASOURCE_SUFFIX.length()
&& name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
}
// 3. 不然,以datasource.做爲前綴進行拼接,如demo-->datasource.demo
return "datasource." + name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
若是是主數據源,返回datasource.primary
若是DataSource對應的id 長度大於dataSource的長度,而且是dataSource結尾的,則截取以前的做爲id,如:demoDataSource–> demo
不然,以datasource.做爲前綴進行拼接,如demo–>datasource.demo
得到DataSource 所對應的DataSourcePoolMetadata,放入metadataByPrefix 中.代碼以下:

public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider
.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
依次遍歷持有的providers,若是能根據給定的DataSource得到DataSourcePoolMetadata,則直接返回,不然返回null.

metrics,實現以下:

public Collection<Metric<?>> metrics() {
Set<Metric<?>> metrics = new LinkedHashSet<Metric<?>>();
// 1. 遍歷metadataByPrefix
for (Map.Entry<String, DataSourcePoolMetadata> entry : this.metadataByPrefix
.entrySet()) {
String prefix = entry.getKey();
// 1.1 得到前綴,若是前綴不是.結尾的,則加上.
prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
DataSourcePoolMetadata metadata = entry.getValue();
// 1.2 添加Metric,name=prefix.active value = 已經在使用中的(激活)連接或者返回null,若是該信息不可用的話
addMetric(metrics, prefix + "active", metadata.getActive());
// 1.3 添加Metric,name=prefix.usage value = 當前數據庫鏈接池的使用量,返回值在0至1之間(或者是-1,若是當前數據庫鏈接池沒有限制的話)
addMetric(metrics, prefix + "usage", metadata.getUsage());
}
return metrics;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
遍歷metadataByPrefix

得到前綴,若是前綴不是.結尾的,則加上.
添加Metric,name=prefix.active value = 已經在使用中的(激活)連接或者返回null,若是該信息不可用的話
添加Metric,name=prefix.usage value = 當前數據庫鏈接池的使用量,返回值在0至1之間(或者是-1,若是當前數據庫鏈接池沒有限制的話)
addMetric代碼以下:

private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name,
T value) {
if (value != null) {
metrics.add(new Metric<T>(name, value));
}
}
1
2
3
4
5
6
自動裝配:

聲明在DataSourceMetricsConfiguration中.代碼以下:

@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
static class DataSourceMetricsConfiguration {

@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSourcePoolMetadataProvider.class)
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
因爲在DataSourceMetricsConfiguration上聲明瞭以下註解:

@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
1
2
3
所以在知足以下條件時該配置生效:

@ConditionalOnClass(DataSource.class)–> 在類路徑下存在DataSource.class時生效
@ConditionalOnBean(DataSource.class) –> 在beanFactory中存在DataSource類型的bean時生效
因爲在dataSourcePublicMetrics聲明瞭 @Conditional 註解,所以知足以下條件時生效:

@ConditionalOnMissingBean–> 在beanFactory中不存在DataSourcePublicMetrics類型的bean時生效@ConditionalOnBean(DataSourcePoolMetadataProvider.class)–> 當在beanFactory中存在DataSourcePoolMetadataProvider類型的bean時生效--------------------- 版權聲明:本文爲CSDN博主「一個努力的碼農」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/qq_26000415/article/details/79134941

相關文章
相關標籤/搜索