如何做自己的服务监控?spring boot 1.x服务监控揭秘

1.准备

下载可运行程序:http://www.mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

2.添加服务监控依赖

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

3.启动spring boot项目

console 截图如下:

如何做自己的服务监控?spring boot 1.x服务监控揭秘

4.servlet和filter

4.1 使用ServletRegistrationBean注册dispatcherServlet

/**
* A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+
* container. Similar to the {@link ServletContext#addServlet(String, Servlet)
* registration} features provided by {@link ServletContext} but with a Spring Bean
* friendly design.
* <p>
* The {@link #setServlet(Servlet) servlet} must be specified before calling
* {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or
* omitted when mapping to '/*' (unless
* {@link #ServletRegistrationBean(Servlet, boolean, String...) alwaysMapUrl} is set to
* {@code false}). The servlet name will be deduced if not specified.
*
* @param <T> the type of the {@link Servlet} to register
* @author Phillip Webb
* @since 1.4.0
* @see ServletContextInitializer
* @see ServletContext#addServlet(String, Servlet)
*/

总结:类似于ServletContext#addServlet(String, Servlet)

查看所有注册的bean

http://127.0.0.1:8080/beans

并把返回的json 格式化 视图查看,在线工具(http://www.bejson.com/jsonviewernew/)

如何做自己的服务监控?spring boot 1.x服务监控揭秘

注册的流程:

spring-boot-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=DispatcherServletAutoConfiguration

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
dispatcherServlet, this.webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}

4.2 使用FilterRegistrationBean注册各种filter

/**
* A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+
* container. Similar to the {@link ServletContext#addFilter(String, Filter) registration}
* features provided by {@link ServletContext} but with a Spring Bean friendly design.
* <p>
* The {@link #setFilter(Filter) Filter} must be specified before calling
* {@link #onStartup(ServletContext)}. Registrations can be associated with
* {@link #setUrlPatterns URL patterns} and/or servlets (either by {@link #setServletNames
* name} or via a {@link #setServletRegistrationBeans ServletRegistrationBean}s. When no
* URL pattern or servlets are specified the filter will be associated to '/*'. The filter
* name will be deduced if not specified.
*
* @param <T> the type of {@link Filter} to register
* @author Phillip Webb
* @since 1.4.0
* @see ServletContextInitializer
* @see ServletContext#addFilter(String, Filter)
* @see DelegatingFilterProxyRegistrationBean
*/

总结:类似于ServletContext#addFilter(String, Filter)

spring-boot-actuator-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=WebMvcMetricsAutoConfiguration,......

5.RequestMappingHandlerAdapter查找controller注解

使用

2019-01-16 09:47:07.715  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@66ac5762: startup date [Wed Jan 16 09:47:06 CST 2019]; root of context hierarchy

定义:

/**
* An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s
* with their method argument and return type signature, as defined via
* {@code @RequestMapping}.
*
* <p>Support for custom argument and return value types can be added via
* {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
* Or alternatively, to re-configure all argument and return value types,
* use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
* @see HandlerMethodArgumentResolver
* @see HandlerMethodReturnValueHandler
*/

内部实现源码

private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
} List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(beans); List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>(); for (ControllerAdviceBean bean : beans) {
Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(bean, attrMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @ModelAttribute methods in " + bean);
}
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(bean, binderMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @InitBinder methods in " + bean);
}
}
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (logger.isInfoEnabled()) {
logger.info("Detected RequestBodyAdvice bean in " + bean);
}
}
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice bean in " + bean);
}
}
} if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}

6.使用RequestMappingHandlerMapping查找controller映射路径

2019-01-16 09:47:07.758  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.mkyong.WelcomeController.welcome(java.util.Map<java.lang.String, java.lang.Object>)

定义:

/**
* Creates {@link RequestMappingInfo} instances from type and method-level
* {@link RequestMapping @RequestMapping} annotations in
* {@link Controller @Controller} classes.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 3.1
*/

作用:使用注解@RequestMapping在controller类内创建一个类型或者方法级别的RequestMappingInfo实例

7.EndpointHandlerMapping映射的监控项

2019-01-15 14:19:30.985  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.986 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
2019-01-15 14:19:30.987 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2019-01-15 14:19:30.987 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.988 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.988 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
2019-01-15 14:19:30.989 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.990 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.992 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2019-01-15 14:19:30.992 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.995 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.996 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.996 INFO 9440 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()

其中,

/trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

/health通过HealthMvcEndpoint.invoke()触发

/metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

/metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

/dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

/heapdump通过HeapdumpMvcEndpoint.invoke()触发

/beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

/autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

/env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

/info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

/mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法

小结:

restful请求实现分两种,一种通过EndpointMvcAdapter.invoke()触发

如何做自己的服务监控?spring boot 1.x服务监控揭秘

而EndpointMvcAdapter.invoke()通过注解@GetMapping实现了restful服务

/**
* Adapter class to expose {@link Endpoint}s as {@link MvcEndpoint}s.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> { /**
* Create a new {@link EndpointMvcAdapter}.
* @param delegate the underlying {@link Endpoint} to adapt.
*/
public EndpointMvcAdapter(Endpoint<?> delegate) {
super(delegate);
} @Override
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Object invoke() {
return super.invoke();
} }

另一种,通过继承MvcEndpoint的invoke方法来触发

如何做自己的服务监控?spring boot 1.x服务监控揭秘

例如HealthMvcEndpoint

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Object invoke(Principal principal) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return getDisabledResponse();
}
Health health = getHealth(principal);
HttpStatus status = getStatus(health);
if (status != null) {
return new ResponseEntity<Health>(health, status);
}
return health;
}

7.1 EndpointHandlerMapping的定义

/**
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
* The semantics of {@code @RequestMapping} should be identical to a normal
* {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
* (otherwise they will be mapped by the normal MVC mechanisms).
* <p>
* One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
* can still provide useful service interfaces when there is no HTTP server (and no Spring
* MVC on the classpath). Note that any endpoints having method signatures will break in a
* non-servlet environment.
*
* @author Phillip Webb
* @author Christian Dupuis
* @author Dave Syer
*/

7.2 层次结构

如何做自己的服务监控?spring boot 1.x服务监控揭秘

7.3 查看对应的bean的生成

      {
"bean": "endpointHandlerMapping",
"aliases": [ ],
"scope": "singleton",
"type": "org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping",
"resource": "class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]",
"dependencies": [ ]
}

7.4 获取流程

    @Bean
@ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() {
Set<? extends MvcEndpoint> endpoints = mvcEndpoints().getEndpoints(); //1
CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
corsConfiguration); //2
boolean disabled = this.managementServerProperties.getPort() != null
&& this.managementServerProperties.getPort() == -1;
mapping.setDisabled(disabled);
if (!disabled) {
mapping.setPrefix(this.managementServerProperties.getContextPath()); //3
}
if (this.mappingCustomizers != null) {
for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(mapping); //4
}
}
return mapping;
}

7.4.1 MvcEndpoints获取endpoint定义

    @Override
public void afterPropertiesSet() throws Exception {
Collection<MvcEndpoint> existing = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
.values();
this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes")
Collection<Endpoint> delegates = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
.values();
for (Endpoint<?> endpoint : delegates) {
if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
String path = determinePath(endpoint,
this.applicationContext.getEnvironment());
if (path != null) {
adapter.setPath(path);
}
this.endpoints.add(adapter);
}
}
}

7.4.2 定义映射关系

其内部实现源码:

    private String getPath(Object handler) {
if (handler instanceof String) {
handler = getApplicationContext().getBean((String) handler);
}
if (handler instanceof MvcEndpoint) {
return ((MvcEndpoint) handler).getPath();
}
return "";
}

7.4.3 增加contextpath

7.4.4 自定义EndpointHandlerMappingCustomizer

8.其它通过

    @Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnEnabledEndpoint("env")
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate);
} @Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint("heapdump")
public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
return new HeapdumpMvcEndpoint();
} @Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
ManagementServerProperties managementServerProperties) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
isHealthSecure(), managementServerProperties.getSecurity().getRoles());
if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
}
return healthMvcEndpoint;
} @Bean
@ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnEnabledEndpoint("metrics")
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
return new MetricsMvcEndpoint(delegate);
} @Bean
@ConditionalOnEnabledEndpoint("logfile")
@Conditional(LogFileCondition.class)
public LogFileMvcEndpoint logfileMvcEndpoint() {
return new LogFileMvcEndpoint();
} @Bean
@ConditionalOnBean(ShutdownEndpoint.class)
@ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
}

以health为例

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Object invoke(Principal principal) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return getDisabledResponse();
}
Health health = getHealth(principal);
HttpStatus status = getStatus(health);
if (status != null) {
return new ResponseEntity<Health>(health, status);
}
return health;
}

调用HealthEndpoint

    /**
* Invoke all {@link HealthIndicator} delegates and collect their health information.
*/
@Override
public Health invoke() {
return this.healthIndicator.health();
}

总结:

spring boot提供http请求的方式可以分两种:

1.通过查找@Controller注解中的@RequestMapping来形成HandlerMapping

2.直接通过@RequestMapping来形成HandlerMapping如actuator模块,这里面又分成两种:

2.1  一种集中式的通过继承@RequestMapping来实现如通过EndpointMvcAdapter.invoke()触发

2.2  另一种通过直接的@RequestMapping注解实现

3.spring boot1.x监控的实现

  /trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

  /health通过HealthMvcEndpoint.invoke()触发

  /metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

  /metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

  /dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

  /heapdump通过HeapdumpMvcEndpoint.invoke()触发

  /beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

  /autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

  /env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

  /info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

  /mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法。

4. 这些实现都定义在spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.InfoContributorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration
上一篇:Galgame引擎编写,图像层


下一篇:初探Lambda表达式/Java多核编程【2】并行与组合行为