zoukankan      html  css  js  c++  java
  • 8.SpringBoot学习(八)——Spring Boot Actuator监控

    1.简介

    1.1 概述

    Spring Boot includes a number of additional features to help you monitor and manage your application when you push it to production. You can choose to manage and monitor your application by using HTTP endpoints or with JMX. Auditing, health, and metrics gathering can also be automatically applied to your application.

    Spring Boot包含许多附加功能,可在您将应用程序投入生产时帮助您监控和管理应用程序。您可以选择使用HTTP端点或JMX管理和监控您的应用程序。审计,健康状况和指标收集也可以自动应用于您的应用程序。

    1.2 特点

    Actuator endpoints let you monitor and interact with your application. Spring Boot includes a number of built-in endpoints and lets you add your own. For example, the health endpoint provides basic application health information.

    监控端点使您可以监控应用程序并与之交互。 Spring Boot包含许多内置端点,您可以添加自己的端点。例如,运行健康状况端点提供基本的应用程序运行状况信息。

    The following technology-agnostic endpoints are available:

    ID Description
    auditevents Exposes audit events information for the current application. Requires an AuditEventRepository bean.
    beans Displays a complete list of all the Spring beans in your application.
    caches Exposes available caches.
    conditions Shows the conditions that were evaluated on configuration and auto-configuration classes and the reasons why they did or did not match.
    configprops Displays a collated list of all @ConfigurationProperties.
    env Exposes properties from Spring’s ConfigurableEnvironment.
    flyway Shows any Flyway database migrations that have been applied. Requires one or more Flyway beans.
    health Shows application health information.
    httptrace Displays HTTP trace information (by default, the last 100 HTTP request-response exchanges). Requires an HttpTraceRepository bean.
    info Displays arbitrary application info.
    integrationgraph Shows the Spring Integration graph. Requires a dependency on spring-integration-core.
    loggers Shows and modifies the configuration of loggers in the application.
    liquibase Shows any Liquibase database migrations that have been applied. Requires one or more Liquibase beans.
    metrics Shows ‘metrics’ information for the current application.
    mappings Displays a collated list of all @RequestMapping paths.
    scheduledtasks Displays the scheduled tasks in your application.
    sessions Allows retrieval and deletion of user sessions from a Spring Session-backed session store. Requires a Servlet-based web application using Spring Session.
    shutdown Lets the application be gracefully shutdown. Disabled by default.
    threaddump Performs a thread dump.

    If your application is a web application (Spring MVC, Spring WebFlux, or Jersey), you can use the following additional endpoints:

    ID Description
    heapdump Returns an hprof heap dump file.
    jolokia Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux). Requires a dependency on jolokia-core.
    logfile Returns the contents of the logfile (if logging.file.name or logging.file.path properties have been set). Supports the use of the HTTP Range header to retrieve part of the log file’s content.
    prometheus Exposes metrics in a format that can be scraped by a Prometheus server. Requires a dependency on micrometer-registry-prometheus.

    2.演示环境

    1. JDK 1.8.0_201
    2. Spring Boot 2.2.0.RELEASE
    3. 构建工具(apache maven 3.6.3)
    4. 开发工具(IntelliJ IDEA )

    3.演示代码

    3.1 代码说明

    开启 endpoint,使用 http 进行访问

    3.2 代码结构

    image-20200719175106586

    3.3 maven 依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    

    3.4 配置文件

    info.app.version=1.0.0
    info.app.name=spring-boot-actuator
    info.app.test=test
    
    # 启动所有endpoint
    management.endpoints.web.exposure.include=*
    # 显示详细信息
    management.endpoint.health.show-details=always
    
    # 关闭应用程序
    management.endpoint.shutdown.enabled=true
    

    3.5 java代码

    HelloController.java

    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        public String hello() {
            return "hello world";
        }
    }
    

    3.6 git 地址

    spring-boot/spring-boot-05-basis/spring-boot-actuator

    4.效果展示

    启动 SpringBootActuatorApplication.main 方法,在 spring-boot-actuator.http 访问下列地址,观察输出信息。

    ### GET /actuator
    GET http://localhost:8080/actuator
    

    请求响应信息如下

    GET http://localhost:8080/actuator
    
    HTTP/1.1 200 
    Content-Type: application/vnd.spring-boot.actuator.v3+json
    Transfer-Encoding: chunked
    Date: Sun, 19 Jul 2020 10:05:32 GMT
    
    {
      "_links": {
        "self": {
          "href": "http://localhost:8080/actuator",
          "templated": false
        },
        "beans": {
          "href": "http://localhost:8080/actuator/beans",
          "templated": false
        },
        "caches-cache": {
          "href": "http://localhost:8080/actuator/caches/{cache}",
          "templated": true
        },
        "caches": {
          "href": "http://localhost:8080/actuator/caches",
          "templated": false
        },
        "health": {
          "href": "http://localhost:8080/actuator/health",
          "templated": false
        },
        "health-path": {
          "href": "http://localhost:8080/actuator/health/{*path}",
          "templated": true
        },
        "info": {
          "href": "http://localhost:8080/actuator/info",
          "templated": false
        },
        "conditions": {
          "href": "http://localhost:8080/actuator/conditions",
          "templated": false
        },
        "shutdown": {
          "href": "http://localhost:8080/actuator/shutdown",
          "templated": false
        },
        "configprops": {
          "href": "http://localhost:8080/actuator/configprops",
          "templated": false
        },
        "env": {
          "href": "http://localhost:8080/actuator/env",
          "templated": false
        },
        "env-toMatch": {
          "href": "http://localhost:8080/actuator/env/{toMatch}",
          "templated": true
        },
        "loggers": {
          "href": "http://localhost:8080/actuator/loggers",
          "templated": false
        },
        "loggers-name": {
          "href": "http://localhost:8080/actuator/loggers/{name}",
          "templated": true
        },
        "heapdump": {
          "href": "http://localhost:8080/actuator/heapdump",
          "templated": false
        },
        "threaddump": {
          "href": "http://localhost:8080/actuator/threaddump",
          "templated": false
        },
        "metrics": {
          "href": "http://localhost:8080/actuator/metrics",
          "templated": false
        },
        "metrics-requiredMetricName": {
          "href": "http://localhost:8080/actuator/metrics/{requiredMetricName}",
          "templated": true
        },
        "scheduledtasks": {
          "href": "http://localhost:8080/actuator/scheduledtasks",
          "templated": false
        },
        "mappings": {
          "href": "http://localhost:8080/actuator/mappings",
          "templated": false
        }
      }
    }
    
    Response code: 200; Time: 48ms; Content length: 1659 bytes
    

    5.源码分析

    5.1 web endpoint 请求原理

    spring boot actuator 中有很多 endpoints,这里以 web endpoints 来简单分析一下原理。

    spring-boot-starter-actuator.jar 依赖于 spring-boot-starter-actuator-autoconfigure.jar,spring-boot-starter-actuator-autoconfigure.jar 的结构如下,其中有用于自动装配的 spring.factories、源码、元数据的json信息以及元数据的配置信息。

    image-20200719182234844

    在 spring.factories 中配置了很多的自动装配类,其中有一个叫 WebMvcEndpointManagementContextConfiguration,这个类中声明了 WebMvcEndpointHandlerMapping 的Bean。

    @ManagementContextConfiguration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @ConditionalOnBean({ DispatcherServlet.class, WebEndpointsSupplier.class })
    @EnableConfigurationProperties(CorsEndpointProperties.class)
    public class WebMvcEndpointManagementContextConfiguration {
    
        @Bean
        @ConditionalOnMissingBean
        public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
                                                                             ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
                                                                             EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
                                                                             WebEndpointProperties webEndpointProperties, Environment environment) {
            List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
            Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
            allEndpoints.addAll(webEndpoints);
            allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
            allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
            String basePath = webEndpointProperties.getBasePath();
            EndpointMapping endpointMapping = new EndpointMapping(basePath);
            boolean shouldRegisterLinksMapping = StringUtils.hasText(basePath)
                || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT);
            return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public ControllerEndpointHandlerMapping controllerEndpointHandlerMapping(
            ControllerEndpointsSupplier controllerEndpointsSupplier, CorsEndpointProperties corsProperties,
            WebEndpointProperties webEndpointProperties) {
            EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
            return new ControllerEndpointHandlerMapping(endpointMapping, controllerEndpointsSupplier.getEndpoints(),
                                                        corsProperties.toCorsConfiguration());
        }
    
    }
    

    WebMvcEndpointHandlerMapping 的类图如下

    image-20200719183030011

    它由一个内部类 WebMvcLinksHandler,实现了 links 方法,通过 request 请求中的 url 找到对应的 handler,这里的 linksResolver 是一个 EndpointLinksResolver,他里面包含所有已经开启的 endpoint 信息

    public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerMapping {
    
        private final EndpointLinksResolver linksResolver;
    
        /**
    	 * Creates a new {@code WebMvcEndpointHandlerMapping} instance that provides mappings
    	 * for the given endpoints.
    	 * @param endpointMapping the base mapping for all endpoints
    	 * @param endpoints the web endpoints
    	 * @param endpointMediaTypes media types consumed and produced by the endpoints
    	 * @param corsConfiguration the CORS configuration for the endpoints or {@code null}
    	 * @param linksResolver resolver for determining links to available endpoints
    	 * @param shouldRegisterLinksMapping whether the links endpoint should be registered
    	 */
        public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, Collection<ExposableWebEndpoint> endpoints,
                                            EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
                                            EndpointLinksResolver linksResolver, boolean shouldRegisterLinksMapping) {
            super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping);
            this.linksResolver = linksResolver;
            setOrder(-100);
        }
    
        @Override
        protected LinksHandler getLinksHandler() {
            return new WebMvcLinksHandler();
        }
    
        /**
    	 * Handler for root endpoint providing links.
    	 */
        class WebMvcLinksHandler implements LinksHandler {
    
            @Override
            @ResponseBody
            public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) {
                return Collections.singletonMap("_links",
                                                WebMvcEndpointHandlerMapping.this.linksResolver.resolveLinks(request.getRequestURL().toString()));
            }
    
            @Override
            public String toString() {
                return "Actuator root web endpoint";
            }
        }
    }
    

    WebMvcEndpointHandlerMapping 继承自 AbstractWebMvcEndpointHandlerMapping,AbstractWebMvcEndpointHandlerMapping 继承自 RequestMappingInfoHandlerMapping,最终继承自 AbstractHandlerMethodMapping。AbstractHandlerMethodMapping 实现了 InitializingBean 接口,重写了 afterPropertiesSet(),所以在初始化的时候会调用该方法。该方法实现如下

    @Override
    public void afterPropertiesSet() {
       initHandlerMethods();
    }
    

    在 AbstractWebMvcEndpointHandlerMapping 中进行了重写

    @Override
    protected void initHandlerMethods() {
       for (ExposableWebEndpoint endpoint : this.endpoints) {
          for (WebOperation operation : endpoint.getOperations()) {
             registerMappingForOperation(endpoint, operation);
          }
       }
       if (this.shouldRegisterLinksMapping) {
          registerLinksMapping();
       }
    }
    

    在 registerLinksMapping 方法中将映射关系保存起来

    private void registerLinksMapping() {
        PatternsRequestCondition patterns = patternsRequestConditionForPattern("");
        RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(RequestMethod.GET);
        ProducesRequestCondition produces = new ProducesRequestCondition(this.endpointMediaTypes.getProduced()
                                                                         .toArray(StringUtils.toStringArray(this.endpointMediaTypes.getProduced())));
        RequestMappingInfo mapping = new RequestMappingInfo(patterns, methods, null, null, null, produces, null);
    	// 获取 handler
        LinksHandler linksHandler = getLinksHandler();
        // 注册映射关系
        registerMapping(mapping, linksHandler, ReflectionUtils.findMethod(linksHandler.getClass(), "links",
                                                                          HttpServletRequest.class, HttpServletResponse.class));
    }
    

    这里的 getLinksHandler 获取的 handler 即为上面提到的 WebMvcLinksHandler,最终调用 registerMapping 方法保存到 mappingRegistry 中

    public void registerMapping(T mapping, Object handler, Method method) {
       if (logger.isTraceEnabled()) {
          logger.trace("Register "" + mapping + "" to " + method.toGenericString());
       }
       this.mappingRegistry.register(mapping, handler, method);
    }
    

    image-20200719184717237

    在请求 http://localhost:8080/actuator 时,通过 DispatcherServlet 获取到一个 HandlerMapping,即为 AbstractWebMvcEndpointHandlerMapping,然后再调用 invokeHandlerMethod 方法时,最终调用到

    WebMvcEndpointHandlerMapping.WebMvcLinksHandler#links 方法,它的实现如下

    @Override
    @ResponseBody
    public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) {
       return Collections.singletonMap("_links",
             WebMvcEndpointHandlerMapping.this.linksResolver.resolveLinks(request.getRequestURL().toString()));
    }
    

    最终返回 endpoints 对应的映射关系

    image-20200719185348091

    根据这里的返回结果,会调用到 HandlerMethodReturnValueHandlerComposite#handleReturnValue ,选择一个具体的 Handler 实现为 RequestResponseBodyMethodProcessor,最终调用 它的 handleReturnValue 方法

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
    
        // Try even with null return value. ResponseBodyAdvice could get involved.
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
    

    最终选择合适的 MessageConverter 将范湖值进行输出

    protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
          throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
       // ..
    
       if (selectedMediaType != null) {
          selectedMediaType = selectedMediaType.removeQualityValue();
          for (HttpMessageConverter<?> converter : this.messageConverters) {
             GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                   (GenericHttpMessageConverter<?>) converter : null);
             if (genericConverter != null ?
                   ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                   converter.canWrite(valueType, selectedMediaType)) {
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                      (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                      inputMessage, outputMessage);
                if (body != null) {
                   Object theBody = body;
                   LogFormatUtils.traceDebug(logger, traceOn ->
                         "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                   addContentDispositionHeader(inputMessage, outputMessage);
                   if (genericConverter != null) {
                      genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                   }
                   else {
                      ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                   }
                }
                else {
                   if (logger.isDebugEnabled()) {
                      logger.debug("Nothing to write: null body");
                   }
                }
                return;
             }
          }
       }
    
       // ...
    }
    

    6.参考

    1. 官方文档-Spring Boot Features/Actuator
  • 相关阅读:
    docker 安装 nexus3 初始密码不再是admin123
    eclipse中Tomcat修改项目名称
    WAMP3.1.3自定义根目录
    git学习笔记
    小米和MAC触摸板手势汇总
    IDEA快捷键汇总
    servelet 实现Post接口访问
    LeetCode:Jump Game II
    LeetCode:Trapping Rain Water
    LeetCode: Container With Most Water
  • 原文地址:https://www.cnblogs.com/col-smile/p/13348210.html
Copyright © 2011-2022 走看看