Spring-Boot:Actuator


概述

  • 引入Actuator
  • 使用 Actuator endpoints
  • 自定义 Actuator endpoints
  • Securing Actuator

引入Actuator

1. 概述

  • Actuator 可以实现对程序内部运行情况监控,比如监控状况,Bean加载情况,环境变量,日志信息,线程信息等
  • 通过Actuator公开的端点,我们可以获取到Spring Boot应用运行时的一些状态信息
    • 应用环境中哪些配置属性可用
    • 应用中各个包的日志环境级别
    • 应用正在消耗多少内存
    • HTTP端点被请求了多少次
    • 应用与其协调的外部服务的运行状况如何

2. 开始

  • *依赖: * pom.xml中添加Actuator的Maven依赖

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  • 端点: ** Actuator一旦引入,应用将会提供几个端点以供查询应用信息(默认base-path为/actuator,即端点需要加此前缀使用**,如使用/actuator/health),大多端点默认都是关闭的,需要通过配置开启,以下除了/heapdump之外,其他所有HTTP端点也作为JMX MBeans公开

    请求方式 path(端点) 描述 默认是否开启
    GET /auditevents 生成任何已经触发的audit events的报告 No
    GET /beans 显示Spring应用上下文中的所有bean No
    GET /conditions 生成通过和失败的autoconfiguration conditions,
    并指向应用上下文创建的bean
    No
    GET /configprops 显示所有配置属性及其当前的值 No
    GET,POST
    ,DELETE
    /env 生成应用可用的所有属性源及其属性报告 No
    GET /env/{toMatch} 显示指定的单个环境变量的值 No
    GET /health 返回应用程序及其外部依赖应用的总的运行状况 Yes
    GET /heapdump 下载heap dump No
    GET /httptrace 生成最近100个请求的信息 No
    GET /info 返回应用的任何developer-defined信息 Yes
    GET /loggers 生成包的列表及其对应的配置的日志级别 No
    GET,POST /loggers/{name} 返回给定的日志记录器的已配置且有效的日志级别,
    可以通过POST请求设置有效日志级别
    No
    GET /mappings 生成报告,包含所有HTTP映射及其相应的处理方法 No
    GET /metrics 返回所有metrics(度量)类别的列表 No
    GET /metrics/{name} 返回给定的metrics的多维set值集 No
    GET /scheduledtasks 列出所有计划任务(scheduled tasks) No
    GET /threaddump 返回所有应用线程的报告 No

3. 配置Actuator endpoints

3.1 base-path配置
  • *base-path: * 默认情况下所有端点的根路径为/actuator,如/health端点应使用/actuator/health,如果想自定义(如/management)则需要以下配置

    management:
      endpoints:
        web:
          base-path: /management
3.2 启用或关闭端点
  • 通过2的表格可以发现,除了/health/info两个端点默认开启之外,其他的(由于涉及到敏感信息)默认都是关闭的,如果要开启这些端点则需要配置,考虑安全需求,可以配合Spring Security使用

  • *开启指定端点: *

    management:
      endpoints:
        web:
          exposure:
            include: health,info,beans,conditions
  • *通配符()开启所有端点: **

    management:
      endpoints:
        web:
          exposure:
            include: '*'
  • *排除指定端点: *

    management:
      endpoints:
        web:
          exposure:
            include: '*'
            exclude: threaddump,heapdump #排除此端点

使用Actuator endpoints

  • */actuator * 通过http://localhost:8762/actuator获取到以下信息,可以查看目前开启的HTTP端点

    {
        "_links": {
            "self": {
                "href": "http://localhost:8762/actuator",
                "templated": false
            },
            "health": {
                "href": "http://localhost:8762/actuator/health",
                "templated": false
            },
            "health-path": {
                "href": "http://localhost:8762/actuator/health/{*path}",
                "templated": true
            },
            "info": {
                "href": "http://localhost:8762/actuator/info",
                "templated": false
            },
            "refresh": {
                "href": "http://localhost:8762/actuator/refresh",
                "templated": false
            }
        }
    }
  • /info 通过http://localhost:8762/actuator/info可以获取到application.yml中配置的info信息(或者实现了InfoContributor接口定义的info信息)

  • /health 通过http://localhost:8762/actuator/health可以获取到系统的状态

    • 一共有4种状态

      状态 描述
      UP 外部系统启动并可访问,所有都满足时为此状态
      DOWN 外部系统已关闭或无法访问,存在一个即为此状态
      UNKNOWN 外部系统状态不清楚,存在时将被忽略
      OUT_OF_SERVICE 外部系统可访问,单目前不可用,存在一个即为此状态
    • 默认情况下仅仅返回状态信息

      {"status":"UP"}
    • 如果要返回详细信息则需要在配置文件中配置

      • 配置

        management:
          endpoint:
            health:
              show-details: always
      • 返回详细信息,可以看到diskSpace属性,所有应用程序的都会有此属性它表示磁盘运行状况,如果可用磁盘空间低于阈值,则状态会变为DOWN

        {
            "status": "UP",
            "components": {
                "db": {
                    "status": "UP",
                    "details": {
                        "database": "H2",
                        "validationQuery": "isValid()"
                    }
                },
                "discoveryComposite": {
                    "status": "UP",
                    "components": {
                        "discoveryClient": {
                            "status": "UP",
                            "details": {
                                "services": []
                            }
                        },
                        "eureka": {
                            "description": "Eureka discovery client has not yet successfully connected to a Eureka server",
                            "status": "UP",
                            "details": {
                                "applications": {}
                            }
                        }
                    }
                },
                "diskSpace": {
                    "status": "UP",
                    "details": {
                        "total": 499839078400,
                        "free": 450688933888,
                        "threshold": 10485760,
                        "exists": true
                    }
                },
                "hystrix": {
                    "status": "UP"
                },
                "ping": {
                    "status": "UP"
                },
                "refreshScope": {
                    "status": "UP"
                }
            }
        }
  • /beans 查看系统的bean信息,以下展示部分片段,可以看到discoveryClientHealthIndicator和其注入的依赖

    {
        "contexts": {
            "application-1": {
                "beans": {
                    ...
                    "discoveryClientHealthIndicator": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator",
                        "resource": "class path resource [org/springframework/cloud/client/CommonsClientAutoConfiguration$DiscoveryLoadBalancerConfiguration.class]",
                        "dependencies": ["spring.cloud.discovery.client.health-indicator-org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties"]
                    },
                    ...
                },
                "parentId": null
            }
        }
    }
  • /conditions 返回自动配置报告,包含了positiveMatches(通过的条件配置),negativeMatches(失败的条件配置),unconditionalClasses(无条件类),如以下片段

    {
        "contexts": {
            "application-1": {
                "positiveMatches": {
                    ...
                    "MongoDataAutoConfiguration#mongoTemplate": [{
                        "condition": "OnBeanCondition",
                        "message": "@ConditionalOnMissingBean (types:org.springframework.data.mongodb.core.MongoTemplate;SearchStrategy: all) did not find any beans "
                    }]
                    ...
                },
                "negativeMatches": {
                    ...
                    "DispatcherServletAutoConfiguration": {
                        "notMatched": [{
                            "condition": "OnClassCondition",
                            "message": "@ConditionalOnClass did not find required class 'org.springframework.web.servlet.DispatcherServlet '"
                        }],
                        "matched": []
                    }
                    ...
                },
                "unconditionalClasses": [
                    ...
                    "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration "
                    ...
                ]
            }
        }
    }
  • /env 可以查看环境变量丶JVM系统属性丶application.properties丶application.yml丶甚至Spring Cloud Config Server

    {
        "activeProfiles": [
            "development"
        ],
        "propertySources": [{
                "name": "systemEnvironment",
                "properties": {
                    "PATH": {
                        "value": "/usr/bin:/bin:/usr/sbin:/sbin",
                        "origin": "System Environment Property \"PATH\""
                    },
                    "HOME": {
                        "value": "/Users/habuma",
                        "origin": "System Environment Property \"HOME\""
                    }
                }
            },
            {
                "name": "applicationConfig: [classpath:/application.yml]",
                "properties": {
                    "spring.application.name": {
                        "value": "ingredient-service",
                        "origin": "class path resource [application.yml]:3:11"
                    },
                    "server.port": {
                        "value": 8081,
                        "origin": "class path resource [application.yml]:9:9"
                    }
                }
            }
        ]
    }

自定义 Actuator endpoints

1. 概述

  • 一些Actuator endpoints允许定制,同时也允许创建自定义Actuator endpoints

2. /info端点

2.1 一般信息添加
  • *方式一: *可以通过在application.yml中配置添加自定义信息

    info:
      contact:
        email: [email protected]
        phone: 123
  • *方式二: *实现InfoContributor接口,并使用@Component注解,添加自定义信息

    @Component
    public class TacoInfoContributor implements InfoContributor {
        @Override
        public void contribute(Info.Builder builder) {
            builder.withDetail("name","taco");
        }
    }
2.2 build-info信息添加
  • Spring boot提供了一些内置的InfoContributor实现,如BuildInfoContributor可以将项目构建信息build-info.properties文件内容添加到/info端点中(如版本信息丶主机丶用户丶时间戳),需要在pom.xmlbuild标签中配置启用

    • 配置启用build-info

         <build>
             <plugins>
                 <plugin>
                     <groupId>org.springframework.boot</groupId>
                     <artifactId>spring-boot-maven-plugin</artifactId>
                     <executions>
                         <execution>
                             <goals>
                                 <goal>build-info</goal> <!--启用-->
                             </goals>
                         </execution>
                     </executions>
                 </plugin>
             </plugins>
         </build>
    • 使用IDEA自动生成build-info.properties文件,打开Maven project找到spring-boot -> spring-boot:build-info执行即可自动生成,文件会发布到/META-INF/目录下

      build.artifact=taco
      build.group=cn.com.taco
      build.name=taco
      build.time=2020-09-10T05\:49\:52.959Z
      build.version=0.0.1-SNAPSHOT
2.3 GIT提交信息添加
  • pom.xml中添加插件

        <build>
            <plugins>
                ...
                <plugin>
                    <groupId>pl.project13.maven</groupId>
                    <artifactId>git-commit-id-plugin</artifactId>
                </plugin>
                ...
            </plugins>
        </build>
  • 使用IDEA自动生成git.properties文件,打开Maven project找到Plugins -> git-commit-id -> git-commit-id:version执行即可自动生成,文件会发布到/classes/目录下

    #Generated by Git-Commit-Id-Plugin
    #Thu Sep 10 14:10:58 CST 2020
    git.branch=cloud
    git.build.host=DESKTOP-B2MV9TT
    git.build.time=2020-09-10T14\:10\:58+0800
    git.build.user.email=[email protected]
    git.build.user.name=bryson
    git.build.version=0.0.1-SNAPSHOT
    git.closest.tag.commit.count=
    git.closest.tag.name=
    git.commit.id=a7baf19e542bbd24c8511cd8c202176db637e693
    git.commit.id.abbrev=a7baf19
    git.commit.id.describe=a7baf19-dirty
    git.commit.id.describe-short=a7baf19-dirty
    git.commit.message.full=work cloud
    git.commit.message.short=work cloud
    git.commit.time=2020-09-10T10\:28\:19+0800
    git.commit.user.email=[email protected]
    git.commit.user.name=bryson
    git.dirty=true
    git.local.branch.ahead=0
    git.local.branch.behind=0
    git.remote.origin.url=[email protected]\:BrysonBS/learning.git
    git.tags=
    git.total.commit.count=64
  • 使用http://localhost:8762/actuator/info可以发现只有简略信息(分支和commit时间戳),如果想要显示详细信息则需要在application.yml中配置

    {
        "git": {
            "branch": "cloud",
            "commit": {
                "id": "a7baf19",
                "time": "2020-09-10T02:28:19Z"
            }
        },
        "build": {
            "artifact": "taco",
            "name": "taco",
            "time": "2020-09-10T05:49:52.959Z",
            "version": "0.0.1-SNAPSHOT",
            "group": "cn.com.taco"
        }
    }
  • application.yml中配置,使其显示相信信息

    • application.yml配置

      management:
        info:
          git:
            mode: full
    • 使用http://localhost:8762/actuator/info结果

      {
          "git": {
              "local": {
                  "branch": {
                      "ahead": "0",
                      "behind": "0"
                  }
              },
              "commit": {
                  "id": {
                      "describe-short": "a7baf19-dirty",
                      "abbrev": "a7baf19",
                      "full": "a7baf19e542bbd24c8511cd8c202176db637e693",
                      "describe": "a7baf19-dirty"
                  },
                  "message": {
                      "short": "work cloud",
                      "full": "work cloud"
                  },
                  "user": {
                      "name": "bryson",
                      "email": "[email protected]"
                  },
                  "time": "2020-09-10T02:28:19Z"
              },
              "branch": "cloud",
              "build": {
                  "time": "2020-09-10T06:10:58Z",
                  "version": "0.0.1-SNAPSHOT",
                  "host": "DESKTOP-B2MV9TT",
                  "user": {
                      "name": "bryson",
                      "email": "[email protected]"
                  }
              },
              "tags": "",
              "total": {
                  "commit": {
                      "count": "64"
                  }
              },
              "closest": {
                  "tag": {
                      "commit": {
                          "count": ""
                      },
                      "name": ""
                  }
              },
              "remote": {
                  "origin": {
                      "url": "[email protected]:BrysonBS/learning.git"
                  }
              },
              "dirty": "true"
          },
          "build": {
              "artifact": "taco",
              "name": "taco",
              "time": "2020-09-10T05:49:52.959Z",
              "version": "0.0.1-SNAPSHOT",
              "group": "cn.com.taco"
          }
      }

3. /health端点

  • Spring Boot默认提供了多个health indicators运行状况指示器,可以用于许多集成的外部系统,但是如果没有的情况,可以通过实现HealthIndicator接口自定义指示器

    @Component
    public class WackoHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
            if (hour > 12) {
                return Health
                        .outOfService()
                        .withDetail("reason",
                                "I'm out of service after lunchtime")
                        .withDetail("hour", hour)
                        .build();
            }
            if (Math.random() < 0.1) {
                return Health
                        .down()
                        .withDetail("reason", "I break 10% of the time")
                        .build();
            }
            return Health
                    .up()
                    .withDetail("reason", "All is good!")
                    .build();
        }
    }

4. 注册自定义metrics(指标)

  • 可以通过MeterRegistry进行度量,在类中注入,并使用

    @Component
    public class TacoMetrics {
        private MeterRegistry meterRegistry;
        //spring4.X以后构造函数参数可以自动注入,无需@Autowired
        public TacoMetrics(MeterRegistry meterRegistry) {
            this.meterRegistry = meterRegistry;
            meterRegistry.counter("taco","test1","test2").increment();//统计,只有一次
        }
    }
  • 使用http://localhost:8762/actuator/metrics/taco查看结果

    {
        "name": "taco",
        "description": null,
        "baseUnit": null,
        "measurements": [{
            "statistic": "COUNT",
            "value": 0.0
        }],
        "availableTags": [{
            "tag": "test1",
            "values": ["test2"]
        }]
    }

5. 创建自定义端点(endpoints)

  • 与创建Controller类似,但是因为支持JMX MBeans所以有额外的内容

  • 使用到的注解@Endpoint(类似@Controller)丶@ReadOperation@WriteOperation@DeleteOperation,这些注解并不代表通信机制,因为支持HTTPJMX

  • 使用@Endpoint会公开HTTP端点和JMX,如果只想公开HTTP端点则可以使用@WebEndpoint,如果只想公开JMX则可以使用@JmxEndpoint注解

  • 实例

    @Component
    @Endpoint(id = "notes",enableByDefault = true)
    //默认为true,如果为false,则可以通过配置属性management.web.endpoints.web.exposure.include开启
    //即使为true,如果已经配置management.web.endpoints.web.exposure.include属性,则需要额外开启才行
    public class NotesEndpoints {
        @ReadOperation //读操作: HTTP中对应GET请求
        public String notes(){
            return "读: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        }
        @WriteOperation //写操作: HTTP中对应POST请求
        public String addNotes(){
            return "写: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        }
        @DeleteOperation//删除操作: HTTP中对应DELETE请求
        public String deleteNotes(){
            return "删除: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        }
    }

Securing Actuator

1. 概述

  • Actuator提供的信息可能不希望任何人都能看到,或者有些端点可以修改配置属性(如日志级别),这些更需要控制权限, 因此需要配合Spring Security使用

2. 使用Spring Security控制权限

  • *不推荐: *由于所有端点的根路径为/actuator,因此可以直接控制根路径权限

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
     .authorizeRequests()
     //使用/actuator/**根路径的缺点是根路径是可以配置的,如果更改了,安全配置就会失效
     .antMatchers("/actuator/**").hasRole("ADMIN") //只有ADMIN角色可以访问
     .and()
     .httpBasic();
    }
  • 使用EndpointRequest替代硬编码路径

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
     .requestMatcher(EndpointRequest.toAnyEndpoint())//替换掉硬编码路径,即使更改配置根路径也不会受到影响
     .authorizeRequests()
     .anyRequest().hasRole("ADMIN")
     .and()
     .httpBasic();
    }
  • 排除部分端点不设置身份验证:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
     .requestMatcher(
     EndpointRequest.toAnyEndpoint()
     .excluding("health", "info")) //排除端点/health和/info不做身份验证
     .authorizeRequests()
     .anyRequest().hasRole("ADMIN")
     .and()
     .httpBasic();
    }
  • 指定验证的端点

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
     .requestMatcher(EndpointRequest.to("beans", "threaddump", "loggers")) //指定要验证的端点
     .authorizeRequests()
     .anyRequest().hasRole("ADMIN")
     .and()
     .httpBasic();
    }

文章作者: Bryson
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryson !
评论
 上一篇
下一篇 
  目录