SpringbootActuator之七:actuator中原⽣endpoint源码解析1
看actuator项⽬的包结构,如下:
本⽂中的介绍Endpoints。
Endpoints(端点)介绍
Endpoints 是 Actuator 的核⼼部分,它⽤来监视应⽤程序及交互,spring-boot-actuator中已经内置了⾮常多的Endpoints(health、info、beans、httptrace、shutdown等等),同时也允许我们扩展⾃⼰的端点。
Endpoints 分成两类:原⽣端点和⽤户⾃定义端点:
1. 原⽣端点是在应⽤程序⾥提供的众多 restful api 接⼝,通过它们可以监控应⽤程序运⾏时的内部状况。原⽣端点⼜可以分成三类:
应⽤配置类:可以查看应⽤在运⾏期间的静态信息:例如⾃动配置信息、加载的spring bean信息、yml⽂件配置信息、环境信息、请求映射信息;
度量指标类:主要是运⾏期间的动态信息,例如堆栈、请求连、⼀些健康指标、metrics信息等;
操作控制类:主要是指shutdown,⽤户可以发送⼀个请求将应⽤的监控功能关闭。
2. ⾃定义端点主要是指扩展性,⽤户可以根据⾃⼰的实际应⽤,定义⼀些⽐较关⼼的指标,在运⾏期进⾏监控。
我们这⾥详细说明org.springframework.dpoint中原⽣端点的实现.通过如下⼏个维度来进⾏分析:
xxxEndpoint的作⽤
xxxEndpoint的字段,构造器
xxxEndpoint核⼼⽅法invoke 实现分析
xxxEndpoint如何进⾏配置
xxxEndpoint如何⾃动化装配
在org.springframework.dpoint 中还有2个⼦包-jmx(可通过jmx协议访问),mvc(通过spring mvc 暴露,可通过接⼝进⾏访问,在下篇⽂章进⾏分析).这⾥我们不关注这些,这看org.springframework.dpoint 包下的类,类⽐较多,先看个类图吧,如下:
Endpoint接⼝:org.springframework.dpoint.Endpoint.java
Endpoint接⼝:⼀个端点可以⽤于暴露(系统信息、操作⼊⼝等)信息。通常暴露⽅式是通过spring mvc的,如继承AbstractEndpoint的⽅式实现⾃⼰的endpoint。
public interface Endpoint<T> {
// 端点的逻辑标识(字母、数字和下划线('_') 组成)
String getId();
// 端点是否启⽤
boolean isEnabled();
// 端点是否输出敏感数据
boolean isSensitive();
// 调⽤端点,并返回调⽤结果
T invoke();
}
其中泛型参数T为暴露的数据类型.⽅法的作⽤已经注释。
AbstractEndpoint抽象类
Endpoint的⼀个抽象⼦类:AbstractEndpoint(Endpoint接⼝实现的抽象基类),该类实现了EnvironmentAware,因此, AbstractEndpoint也就持有了Environment。
1、AbstractEndpoint 有如下属性:
// 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”
private static final Pattern ID_PATTERN = Patternpile("\\w+");
// 通过EnvironmentAware接⼝注⼊
private Environment environment;
private String id;
// 是否默认敏感
private final boolean sensitiveDefault;
// 标识该端点是否暴露敏感信息
private Boolean sensitive;
// 是否端点可⽤
private Boolean enabled;
2、AbstractEndpoint⽅法
AbstractEndpoint⽅法实现了Endpoint接⼝中的getId, isEnabled, isSensitive,其中, getId只需返回AbstractEndpoint中的id属性即可,我们分别来看下其他⽅法的实现: 2.1、isEnabled,代码如下:
@Override
public boolean isEnabled() {
return EndpointProperties.vironment, abled);
}
@ConfigurationProperties(prefix = "endpoints")
public class EndpointProperties {
private static final String ENDPOINTS_ENABLED_PROPERTY = "abled";
private static final String ENDPOINTS_SENSITIVE_PROPERTY = "endpoints.sensitive";
public static boolean isEnabled(Environment environment, Boolean enabled) {
//1、如果AbstractEndpoint#enabled属性有值,则使⽤AbstractEndpoint的配置
if (enabled != null) {
return enabled;
}
//如果Environment 不等于null 并且Environment 配置有abled的属性,则返回其配置的值
if (environment != null
&& ainsProperty(ENDPOINTS_ENABLED_PROPERTY)) {
Property(ENDPOINTS_ENABLED_PROPERTY, Boolean.class);
}
//3、如果1和2没有值,则返回默认值true
return true;
}
2.2、isSensitive和isEnabled实现差不多,如下:
@Override
public boolean isSensitive() {
return EndpointProperties.vironment, this.sensitive,
this.sensitiveDefault);
}
@ConfigurationProperties(prefix = "endpoints")
public class EndpointProperties {
private static final String ENDPOINTS_ENABLED_PROPERTY = "abled";
private static final String ENDPOINTS_SENSITIVE_PROPERTY = "endpoints.sensitive";
public static boolean isSensitive(Environment environment, Boolean sensitive,
boolean sensitiveDefault) {
//1、如果abstractEndpoint的sensitive有值,则使⽤这个配置
if (sensitive != null) {
return sensitive;
}
/
/2、如果environment 不等于null 并且 environment中配置有endpoints.sensitive的属性,则返回其配置值
if (environment != null
&& ainsProperty(ENDPOINTS_SENSITIVE_PROPERTY)) {
Property(ENDPOINTS_SENSITIVE_PROPERTY, Boolean.class);
}
//3、返回指定的默认值(默认为false)
return sensitiveDefault;
}
EnvironmentEndpoint
AbstractEndpoint的实现类之EnvironmentEndpoint--敏感数据
1、构造函数
@ConfigurationProperties(prefix = "v")
public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> {
public EnvironmentEndpoint() {
super("env");
}
//调⽤AbstractEndpoint的构造函数
public AbstractEndpoint(String id) {
this(id, true);
}
最终,设置id为env,标识为敏感数据。
2、实现的invoke(),代码如下:
public Map<String, Object> invoke() {
// 1. 先定义空的map返回值
Map<String, Object> result = new LinkedHashMap<String, Object>();
// 2. 将spring boot 中激活的profile 放⼊result中,key --> profile
result.put("profiles", getEnvironment().getActiveProfiles());
// 3. 获得PlaceholderSanitizingPropertyResolver --> 处理占位符,处理敏感数据
PropertyResolver resolver = getResolver();
// 4. 遍历environment 配置的PropertySource,依次处理之
for (Entry<String, PropertySource<?>> entry : getPropertySourcesAsMap()
.entrySet()) {
PropertySource<?> source = Value();
String sourceName = Key();
if (source instanceof EnumerablePropertySource) {
// 4.1 只针对EnumerablePropertySource 类型的PropertySource 进⾏处理--> 依次将属性添加到properties中,
// 如果属性值为string,则在添加前进⾏占位符,数据脱敏的处理
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
Map<String, Object> properties = new LinkedHashMap<String, Object>();
for (String name : PropertyNames()) {
Object property = Property(name);
Object resolved = property instanceof String
? solvePlaceholders((String) property) : property;
//调⽤Sanitizer类进⾏脱敏
properties.put(name, sanitize(name, resolved));
}
// 4.2 后置处理,该⽅法的实现是直接返回原始值,可以通过覆写的⽅式进⾏扩展
properties = postProcessSourceProperties(sourceName, properties);
if (properties != null) {
// 4.3 如果不为空,则添加到result中
result.put(sourceName, properties);
}
}
}
}
处理占位符,处理敏感数据:PlaceholderSanitizingPropertyResolver.java是EnvironmentEndpoint的内部类
public PropertyResolver getResolver() {
// 1. 实例化PlaceholderSanitizingPropertyResolver --> 处理占位符,处理敏感数据
PlaceholderSanitizingPropertyResolver resolver = new PlaceholderSanitizingPropertyResolver(
getPropertySources(), this.sanitizer);
// 2. 设置ignoreUnresolvableNestedPlaceholders 为true
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
return resolver;
}
PlaceholderSanitizingPropertyResolver继承了PropertySourcesPropertyResolver,这样就能对占位符进⾏处理了,⼜因为其内部持有Sanitizer(⽤于敏感数据脱敏),复写了getPropertyAsRawString,这样就
能处理占位符,敏感数据了.代码如下:
@Override
protected String getPropertyAsRawString(String key) {
String value = PropertyAsRawString(key);
return (String) this.sanitizer.sanitize(key, value);
}
3、EnvironmentEndpoint的属性配置,由于EnvironmentEndpoint被@ConfigurationProperties(prefix = “v”)注解,因此可通过如下配置进⾏个性化配置: v.id=env
同时,⼜因为其声明了如下⽅法:
public void keysToSanitize) {
this.sanitizer.setKeysToSanitize(keysToSanitize);
}
因此可以通过v.keys-to-sanitize=xx,xx 来配置对指定的数据进⾏脱敏。
4、EnvironmentEndpoint的⾃动化装配
EnvironmentEndpoint的⾃动化装配是在EndpointAutoConfiguration中,代码如下:
@Configuration
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
@EnableConfigurationProperties(EndpointProperties.class)
public class EndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public EnvironmentEndpoint environmentEndpoint() {
return new EnvironmentEndpoint();
}
@Bean注解:注册1个id为environmentEndpoint,类型为EnvironmentEndpoint的bean
@ConditionalOnMissingBean注解:当beanFactory中不存在EnvironmentEndpoint类型的bean时注册
InfoEndpoint
AbstractEndpoint的实现类之InfoEndpoint,⽤于暴露应⽤信息。
其字段和构造器如下:
private final List<InfoContributor> infoContributors;
public InfoEndpoint(List<InfoContributor> infoContributors) {
super("info", false);
this.infoContributors = infoContributors;
}
其内部持有了BeanFactory中所有InfoContributor类型的bean,其通过构造器注⼊。
2、invoke 实现如下:
public Map<String, Object> invoke() {
Info.Builder builder = new Info.Builder();
for (InfoContributor contributor : this.infoContributors) {
}
Info build = builder.build();
Details();
}
通过遍历其内部的持有infoContributors,因此调⽤其contribute将info的数据添加到Info.Builder中,最后通过Info.Builder构建出Info,返回Info持有的details(建造者模式). Info中的details为Map.
InfoContributor接⼝⽤于向Info$Builder添加信息,关于这部分的内容,我们后续⽂章有分析.这⾥就不在赘述了.
3、InfoEndpoint的属性配置
@ConfigurationProperties(prefix = "endpoints.info")
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
因此可通过如下进⾏配置:
endpoints.info.id=info
endpoints.info.sensitive=true
abled=true
4、InfoEndpoint的⾃动化装配–>在EndpointAutoConfiguration中,代码如下:
@Bean
@ConditionalOnMissingBean
public InfoEndpoint infoEndpoint() throws Exception {
return new InfoEndpoint(this.infoContributors == null
? Collections.<InfoContributor>emptyList() : this.infoContributors);
}
和EnvironmentEndpoint⼀样。
RequestMappingEndpoint:
AbstractEndpoint的实现类之RequestMappingEndpoint,由于RequestMappingEndpoint同时也实现了A
pplicationContextAware接⼝,因此,在初始化该类时会注⼊applicationContext。这个类的作⽤是打印Spring MVC 映射信息。
1、构造函数
public RequestMappingEndpoint() {
super("mappings");
因此, RequestMappingEndpoint的id为 mappings,默认为敏感。
2、invoke 实现如下:
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
// 1. 从handlerMappings中获取HandlerMapping,默认情况下handlerMappings是不存在数据的
extractHandlerMappings(this.handlerMappings, result);
/
/ 2. 从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次将其注册的handler 添加进去.
extractHandlerMappings(this.applicationContext, result);
// 3. 从methodMappings中获取HandlerMapping,默认情况下methodMappings是不存在数据的
hodMappings, result);
// 3. 从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次获得其持有的HandlerMethods,进⾏处理.
extractMethodMappings(this.applicationContext, result);
return result;
}
从applicationContext中获取
protected void extractHandlerMappings(ApplicationContext applicationContext,
Map<String, Object> result) {
if (applicationContext != null) {
Map<String, AbstractUrlHandlerMapping> mappings = applicationContext
.getBeansOfType(AbstractUrlHandlerMapping.class);
for (Entry<String, AbstractUrlHandlerMapping> mapping : Set()) {
Map<String, Object> handlers = Value());
for (Entry<String, Object> handler : Set()) {
result.Key(),
Collections.singletonMap("bean", Key()));
}
}
}
}
获得AbstractUrlHandlerMapping类型的bean,此时有4个:
beanNameHandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
resourceHandlerMapping=org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
faviconHandlerMapping=org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
welcomePageHandlerMapping=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WelcomePageHandlerMapping
依次遍历mappings:
获得AbstractUrlHandlerMapping中注册的handler,key–> path,value–>handler
依次遍历handlerss,存⼊结果集中,存⼊的key–>AbstractUrlHandlerMapping的id,value={bean=AbstractUrlHandlerMapping中注册的handler的路径}
从methodMappings中获取HandlerMapping,默认情况下methodMappings是不存在数据的
从applicationContext中获取AbstractUrlHandlerMapping类型的bean,依次获得其持有的HandlerMethods,进⾏处理.代码如下:
protected void extractMethodMappings(
Collection<AbstractHandlerMethodMapping<?>> methodMappings,
Map<String, Object> result) {
for (AbstractHandlerMethodMapping<?> mapping : methodMappings) {
Map<?, HandlerMethod> methods = HandlerMethods();
for (Map.Entry<?, HandlerMethod> entry : Set()) {
result.put(String.Key()), Collections
.singletonMap("method", String.Value())));
}
}
}
获得AbstractUrlHandlerMapping类型的bean
依次遍历AbstractUrlHandlerMapping中注册的handler,添加⾄结果集中,key–> Handler 映射路径 ,value = {bean = AbstractHandlerMethodMapping的id,method=HandlerMethod} 3、RequestMappingEndpoint的配置
@ConfigurationProperties(prefix = "endpoints.mappings")
public class RequestMappingEndpoint extends AbstractEndpoint<Map<String, Object>>
implements ApplicationContextAware {
从类上的配置注解,可知:
abled= # Enable the endpoint.
endpoints.mappings.id= # Endpoint identifier.
endpoints.mappings.sensitive= # Mark if the endpoint exposes sensitive information.
4、RequestMappingEndpoint的⾃动装配
@Configuration
@ConditionalOnClass(AbstractHandlerMethodMapping.class)
protected static class RequestMappingEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
public RequestMappingEndpoint requestMappingEndpoint() {
RequestMappingEndpoint endpoint = new RequestMappingEndpoint();
return endpoint;
}
}
当满⾜如下两个条件时创建requestMappingEndpoint,即注册1个id为requestMappingEndpoint,类型为RequestMappingEndpoint的bean:
@ConditionalOnClass(AbstractHandlerMethodMapping.class) –> 在beanFactory中存在AbstractHandlerMethodMapping类型的bean时⽣效
@ConditionalOnMissingBean–>在beanFactory中不存在RequestMappingEndpoint类型的bean时⽣效
DumpEndpoint
AbstractEndpoint的实现类之DumpEndpoint,这个类的作⽤是打印线程信息,为敏感。
1、构造函数
public DumpEndpoint() {
super("dump");
}
2、invoke()⽅法实现
@Override
public List<ThreadInfo> invoke() {
return Arrays
.ThreadMXBean().dumpAllThreads(true, true));
}
调⽤了ThreadMXBean的dumpAllThreads来返回所有活动线程的线程信息,并带有堆栈跟踪和同步信息。当此⽅法返回时,返回数组中包含的⼀些线程可能已经终⽌。其中两个参数指的意义如下:
第2个–>如果为 true,则转储所有锁定的可拥有同步器。
ThreadMXBean怎么读取Thread信息见《》
3、DumpEndpoint的配置
@ConfigurationProperties(prefix = "endpoints.dump")
public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
可知:
abled= # Enable the endpoint.
endpoints.dump.id= # Endpoint identifier.
endpoints.dump.sensitive= # Mark if the endpoint exposes sensitive information.
4、DumpEndpoint⾃动化装配:
@Bean
@ConditionalOnMissingBean
public DumpEndpoint dumpEndpoint() {
return new DumpEndpoint();
}
FlywayEndpoint
AbstractEndpoint的实现类之FlywayEndpoint,Flyway是⼀款开源的数据库版本管理⼯具。
ShutdownEndpoint
AbstractEndpoint的实现类之ShutdownEndpoint,作⽤是关闭应⽤。这个类继承了ApplicationAware,得到applicationContext,关闭spring容器调⽤applicationContext.close()⽅法。
1、构造函数
public ShutdownEndpoint() {
super("shutdown", true, false);
}
id为shutdown,为敏感,关闭应⽤的endpoin默认是关闭的,不启⽤的。
2、invoke()⽅法实现
@Override
public Map<String, Object> invoke() {
//如果context为null,直接返回
if (t == null) {
return NO_CONTEXT_MESSAGE;
}
//context不为null,先返回,再启动⼀个线程,在该线程中,先sleep 5秒后,然后调⽤了ShutdownEndpoint中持有的context的close⽅法进⾏关闭.
try {
return SHUTDOWN_MESSAGE;
}
finally {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
t.close();
}
});
thread.setContextClassLoader(getClass().getClassLoader());
thread.start();
}
}
在返回后,还希望做点啥,⽤try--finally。
3、ShutdownEndpoint的配置
@ConfigurationProperties(prefix = "endpoints.shutdown")
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
implements ApplicationContextAware {
可知配置有:
abled= # Enable the endpoint.
endpoints.shutdown.id= # Endpoint identifier.
springframework作用
endpoints.shutdown.sensitive= # Mark if the endpoint exposes sensitive information.
4、ShutdownEndpoint⾃动化装配:
@Bean
@ConditionalOnMissingBean
public ShutdownEndpoint shutdownEndpoint() {
return new ShutdownEndpoint();
}
AutoConfigurationReportEndpoint
AbstractEndpoint的实现类之AutoConfigurationReportEndpoint,作⽤是暴露.。
1、构造函数
public AutoConfigurationReportEndpoint() {
super("autoconfig");
}
id为autoconfig,为敏感。
2、invoke()⽅法实现
@Override
public Report invoke() {
return new Report(this.autoConfigurationReport);
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。