第11章 Spring Boot应用监控

来源:互联网 发布:mac关闭某个程序 编辑:程序博客网 时间:2024/06/01 23:27

第11章 Spring Boot应用监控

在实际的生产系统中,我们怎样知道我们的应用运行良好呢?我们往往需要对系统实际运行的情况(各种cpu,io,disk,db,业务功能等指标)进行监控运维。这需要耗费我们不少精力来搞这些工作。在SpringBoot中,我们完全不需要面对这样的难题。

本章主要介绍使用Actuator对Spring Boot应用指标进行监控,以及通过远程shell监控与管理我们的应用。

11.0 Actuator简介

Actuator是spring boot提供的对应用系统的自省和监控功能,Actuator对应用系统本身的自省功能,可以让我们方便快捷的实现线上运维监控的工作。这个有点DevOps的味道。

通过Actuator,我们可以使用数据化的指标去度量我们的应用的运行情况。比如查看系统运行了多少线程,gc的情况,运行的基本参数等等

spring-boot-actuator模块提供了一个监控和管理生产环境的模块,可以使用http、jmx、ssh、telnet等拉管理和监控应用。

随着devops的兴起,以及docker技术的普及,微服务在一定场合会越来越受欢迎。即使不说微服务,springboot这种可以直接内嵌web服务器打成一个jar包的方式,也更符合devops的趋势:打成个jar包,往服务器上一扔,十分方便,自带Actuator,把监控也给省了一大半,真正做到了可以把精力花在刀刃上。

11.1 使用Spring Boot Actuator监控应用

1.使用actuator

添加starter依赖

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

重启应用。Spring Boot Actuator 的自动配置功能,会把Actuator的审计(Auditing)信息、健康(health)信息、各种度量数据采集(metrics gathering)会自动加到应用里面。

Actuator主要暴露的功能如下:

HTTP方法路径描述是否敏感信息GET/autoconfig查看自动配置的使用情况, 显示一个auto-configuration的报告,该报告展示所有auto-configuration候选者及它们被应用或未被应用的原因trueGET/configprops查看配置属性,包括默认配置, 显示一个所有@ConfigurationProperties的整理列表trueGET/beansbean及其关系列表, 显示一个应用中所有Spring Beans的完整列表trueGET/dump打印线程栈trueGET/env查看所有环境变量trueGET/env/{name}查看具体变量值trueGET/health查看应用健康指标, 当使用一个未认证连接访问时显示一个简单的’status’,使用认证连接访问则显示全部信息详情falseGET/info查看应用信息falseGET/mappings查看所有url映射, 即所有@RequestMapping路径的整理列表trueGET/metrics查看应用基本指标trueGET/metrics/{name}查看具体指标truePOST/shutdown关闭应用,允许应用以优雅的方式关闭(默认情况下不启用)trueGET/trace查看基本追踪信息,默认为最新的一些HTTP请求true

这些HTTP端点(Endpoint),默认是系统根路径访问的。如果我们想自定义context-path, 按照如下配置即可:

server.port=8888#actuatormanagement.port=58888management.context-path=/actuator

重启应用,我们将会看到如下日志:

01:23:38.033 [localhost-startStop-1] INFO  o.s.b.w.s.FilterRegistrationBean - Mapping filter: 'springSecurityFilterChain' to: [/*] 01:23:38.283 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/info || /actuator/info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.284 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/trace || /actuator/trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.285 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/beans || /actuator/beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.286 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/configprops || /actuator/configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.343 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/mappings || /actuator/mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.348 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/health || /actuator/health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal) 01:23:38.349 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/autoconfig || /actuator/autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.352 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/heapdump || /actuator/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 01:23:38.353 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/dump || /actuator/dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.362 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String) 01:23:38.362 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/metrics || /actuator/metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.363 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String) 01:23:38.363 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/env || /actuator/env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 01:23:38.364 [main] INFO  o.s.b.a.e.m.EndpointHandlerMapping - Mapped "{[/actuator/server || /actuator/server.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()

我们可以清楚的看到Actuator提供应用运行信息监控等相关的HTTP Rest API(Endpoint)。

例如,我们访问 http://localhost:58888/actuator/env , 返回了系统的环境参数信息

// 20170504001108// http://localhost:58888/actuator/env{  "profiles": [  ],  "server.ports": {    "local.management.port": 58888,    "local.server.port": 8888  },  "servletContextInitParams": {  },  "systemProperties": {    "java.runtime.name": "Java(TM) SE Runtime Environment",    "sun.boot.library.path": "/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib",    "java.vm.version": "25.40-b25",    "user.country.format": "CN",   ......    "server.port": "8888",    "spring.jpa.hibernate.naming-strategy": "org.hibernate.cfg.ImprovedNamingStrategy",    "spring.jpa.hibernate.ddl-auto": "update",    "info.build.version": "1.0-SNAPSHOT",    "management.context-path": "/actuator",    "spring.datasource.url": "jdbc:mysql://localhost:3306/lightsword?useUnicode=true&characterEncoding=UTF8",    "endpoints.metrics.enabled": "true",    "endpoints.info.id": "info",    "management.port": "58888",    "spring.velocity.resourceLoaderPath": "classpath:/templates/",    "spring.mvc.static-path-pattern": "/**",    "spring.jpa.database": "MYSQL",    "spring.datasource.min-idle": "0",    "info.app.name": "ecs",    "spring.datasource.max-active": "0",    "spring.datasource.max-wait": "10000",    "management.security.sessions": "stateless",    "endpoints.metrics.id": "metrics",    "management.security.roles": "ADMIN",    "spring.application.name": "lightsword",    "spring.datasource.max-idle": "0",    "spring.datasource.password": "******",    "endpoints.actuator.enabled": "true",    "spring.datasource.username": "root",    "spring.velocity.properties.input.encoding": "UTF-8",    "logging.config": "classpath:logback-dev.groovy",    "endpoints.info.enabled": "true",    "spring.jpa.show-sql": "true",    "spring.velocity.toolbox-config-location": "/WEB-INF/toolbox.xml",    "info.build.artifactId": "lightsword",    "spring.velocity.properties.output.encoding": "UTF-8",    "spring.velocity.suffix": ".html",    "spring.datasource.driverClassName": "com.mysql.jdbc.Driver",    "spring.resources.static-locations": "classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/",    "spring.velocity.charset": "UTF-8",    "info.app.version": "1.0.0"  }}

其他的一些端点,都带有丰富的系统运行数据,感兴趣的可以运行本章节的工程源码,查看端点的数据结构信息。

同时,Actuator支持与security集成,按照如下配置即可启用:

# Enable security.management.security.enabled=true# Comma-separated list of roles that can access the management endpoint.management.security.roles=ADMIN

如果我们使用基于数据库的user,role集成Security的权限认证,经过测试,发现这里的management.security.roles=ADMIN将会不起作用。

如果我们想配置去掉某项的检查,比如不监控health.mail:

server:    port: 8888  management:    port: 58888    health:      mail:        enabled: false

我们可以看出这个58888端口映射的路径也是当前应用的服务器资源。例如http://localhost:58888/js/jsoneditor.js 也能访问到。跟访问 http://localhost:8888/js/jsoneditor.js 是一样的结果。

关于Actuator自动配置的源码在org.springframework.boot.actuate.autoconfigure中。想要详细深入的了解其中的原理,可以阅读这里的源码。

在application.properties中关于 ACTUATOR的配置如下

# ----------------------------------------# ACTUATOR PROPERTIES# ----------------------------------------# ENDPOINTS (AbstractEndpoint subclasses)endpoints.enabled=true # Enable endpoints.endpoints.sensitive= # Default endpoint sensitive setting.endpoints.actuator.enabled=true # Enable the endpoint.endpoints.actuator.path= # Endpoint URL path.endpoints.actuator.sensitive=false # Enable security on the endpoint.endpoints.auditevents.enabled= # Enable the endpoint.endpoints.auditevents.path= # Endpoint path.endpoints.auditevents.sensitive=false # Enable security on the endpoint.endpoints.autoconfig.enabled= # Enable the endpoint.endpoints.autoconfig.id= # Endpoint identifier.endpoints.autoconfig.path= # Endpoint path.endpoints.autoconfig.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.beans.enabled= # Enable the endpoint.endpoints.beans.id= # Endpoint identifier.endpoints.beans.path= # Endpoint path.endpoints.beans.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.configprops.enabled= # Enable the endpoint.endpoints.configprops.id= # Endpoint identifier.endpoints.configprops.keys-to-sanitize=password,secret,key,token,.*credentials.*,vcap_services # Keys that should be sanitized. Keys can be simple strings that the property ends with or regex expressions.endpoints.configprops.path= # Endpoint path.endpoints.configprops.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.docs.curies.enabled=false # Enable the curie generation.endpoints.docs.enabled=true # Enable actuator docs endpoint.endpoints.docs.path=/docs #endpoints.docs.sensitive=false #endpoints.dump.enabled= # Enable the endpoint.endpoints.dump.id= # Endpoint identifier.endpoints.dump.path= # Endpoint path.endpoints.dump.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.env.enabled= # Enable the endpoint.endpoints.env.id= # Endpoint identifier.endpoints.env.keys-to-sanitize=password,secret,key,token,.*credentials.*,vcap_services # Keys that should be sanitized. Keys can be simple strings that the property ends with or regex expressions.endpoints.env.path= # Endpoint path.endpoints.env.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.flyway.enabled= # Enable the endpoint.endpoints.flyway.id= # Endpoint identifier.endpoints.flyway.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.health.enabled= # Enable the endpoint.endpoints.health.id= # Endpoint identifier.endpoints.health.mapping.*= # Mapping of health statuses to HttpStatus codes. By default, registered health statuses map to sensible defaults (i.e. UP maps to 200).endpoints.health.path= # Endpoint path.endpoints.health.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.health.time-to-live=1000 # Time to live for cached result, in milliseconds.endpoints.heapdump.enabled= # Enable the endpoint.endpoints.heapdump.path= # Endpoint path.endpoints.heapdump.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.hypermedia.enabled=false # Enable hypermedia support for endpoints.endpoints.info.enabled= # Enable the endpoint.endpoints.info.id= # Endpoint identifier.endpoints.info.path= # Endpoint path.endpoints.info.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.jolokia.enabled=true # Enable Jolokia endpoint.endpoints.jolokia.path=/jolokia # Endpoint URL path.endpoints.jolokia.sensitive=true # Enable security on the endpoint.endpoints.liquibase.enabled= # Enable the endpoint.endpoints.liquibase.id= # Endpoint identifier.endpoints.liquibase.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.logfile.enabled=true # Enable the endpoint.endpoints.logfile.external-file= # External Logfile to be accessed.endpoints.logfile.path=/logfile # Endpoint URL path.endpoints.logfile.sensitive=true # Enable security on the endpoint.endpoints.loggers.enabled=true # Enable the endpoint.endpoints.loggers.id= # Endpoint identifier.endpoints.loggers.path=/logfile # Endpoint path.endpoints.loggers.sensitive=true # Mark if the endpoint exposes sensitive information.endpoints.mappings.enabled= # Enable the endpoint.endpoints.mappings.id= # Endpoint identifier.endpoints.mappings.path= # Endpoint path.endpoints.mappings.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.metrics.enabled= # Enable the endpoint.endpoints.metrics.filter.enabled=true # Enable the metrics servlet filter.endpoints.metrics.filter.gauge-submissions=merged # Http filter gauge submissions (merged, per-http-method)endpoints.metrics.filter.counter-submissions=merged # Http filter counter submissions (merged, per-http-method)endpoints.metrics.id= # Endpoint identifier.endpoints.metrics.path= # Endpoint path.endpoints.metrics.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.shutdown.enabled= # Enable the endpoint.endpoints.shutdown.id= # Endpoint identifier.endpoints.shutdown.path= # Endpoint path.endpoints.shutdown.sensitive= # Mark if the endpoint exposes sensitive information.endpoints.trace.enabled= # Enable the endpoint.endpoints.trace.filter.enabled=true # Enable the trace servlet filter.endpoints.trace.id= # Endpoint identifier.endpoints.trace.path= # Endpoint path.endpoints.trace.sensitive= # Mark if the endpoint exposes sensitive information.# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties)endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported.endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods.endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response.endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients.# JMX ENDPOINT (EndpointMBeanExportProperties)endpoints.jmx.domain= # JMX domain name. Initialized with the value of 'spring.jmx.default-domain' if set.endpoints.jmx.enabled=true # Enable JMX export of all endpoints.endpoints.jmx.static-names= # Additional static properties to append to all ObjectNames of MBeans representing Endpoints.endpoints.jmx.unique-names=false # Ensure that ObjectNames are modified in case of conflict.# JOLOKIA (JolokiaProperties)jolokia.config.*= # See Jolokia manual# MANAGEMENT HTTP SERVER (ManagementServerProperties)management.add-application-context-header=false # Add the "X-Application-Context" HTTP header in each response.management.address= # Network address that the management endpoints should bind to.management.context-path= # Management endpoint context-path. For instance `/actuator`management.cloudfoundry.enabled= # Enable extended Cloud Foundry actuator endpointsmanagement.cloudfoundry.skip-ssl-validation= # Skip SSL verification for Cloud Foundry actuator endpoint security callsmanagement.port= # Management endpoint HTTP port. Uses the same port as the application by default. Configure a different port to use management-specific SSL.management.security.enabled=true # Enable security.management.security.roles=ACTUATOR # Comma-separated list of roles that can access the management endpoint.management.security.sessions=stateless # Session creating policy to use (always, never, if_required, stateless).management.ssl.ciphers= # Supported SSL ciphers. Requires a custom management.port.management.ssl.client-auth= # Whether client authentication is wanted ("want") or needed ("need"). Requires a trust store. Requires a custom management.port.management.ssl.enabled= # Enable SSL support. Requires a custom management.port.management.ssl.enabled-protocols= # Enabled SSL protocols. Requires a custom management.port.management.ssl.key-alias= # Alias that identifies the key in the key store. Requires a custom management.port.management.ssl.key-password= # Password used to access the key in the key store. Requires a custom management.port.management.ssl.key-store= # Path to the key store that holds the SSL certificate (typically a jks file). Requires a custom management.port.management.ssl.key-store-password= # Password used to access the key store. Requires a custom management.port.management.ssl.key-store-provider= # Provider for the key store. Requires a custom management.port.management.ssl.key-store-type= # Type of the key store. Requires a custom management.port.management.ssl.protocol=TLS # SSL protocol to use. Requires a custom management.port.management.ssl.trust-store= # Trust store that holds SSL certificates. Requires a custom management.port.management.ssl.trust-store-password= # Password used to access the trust store. Requires a custom management.port.management.ssl.trust-store-provider= # Provider for the trust store. Requires a custom management.port.management.ssl.trust-store-type= # Type of the trust store. Requires a custom management.port.# HEALTH INDICATORSmanagement.health.db.enabled=true # Enable database health check.management.health.cassandra.enabled=true # Enable cassandra health check.management.health.couchbase.enabled=true # Enable couchbase health check.management.health.defaults.enabled=true # Enable default health indicators.management.health.diskspace.enabled=true # Enable disk space health check.management.health.diskspace.path= # Path used to compute the available disk space.management.health.diskspace.threshold=0 # Minimum disk space that should be available, in bytes.management.health.elasticsearch.enabled=true # Enable elasticsearch health check.management.health.elasticsearch.indices= # Comma-separated index names.management.health.elasticsearch.response-timeout=100 # The time, in milliseconds, to wait for a response from the cluster.management.health.jms.enabled=true # Enable JMS health check.management.health.ldap.enabled=true # Enable LDAP health check.management.health.mail.enabled=true # Enable Mail health check.management.health.mongo.enabled=true # Enable MongoDB health check.management.health.rabbit.enabled=true # Enable RabbitMQ health check.management.health.redis.enabled=true # Enable Redis health check.management.health.solr.enabled=true # Enable Solr health check.management.health.status.order=DOWN, OUT_OF_SERVICE, UP, UNKNOWN # Comma-separated list of health statuses in order of severity.# INFO CONTRIBUTORS (InfoContributorProperties)management.info.build.enabled=true # Enable build info.management.info.defaults.enabled=true # Enable default info contributors.management.info.env.enabled=true # Enable environment info.management.info.git.enabled=true # Enable git info.management.info.git.mode=simple # Mode to use to expose git information.# TRACING (TraceProperties)management.trace.include=request-headers,response-headers,cookies,errors # Items to be included in the trace.# METRICS EXPORT (MetricExportProperties)spring.metrics.export.aggregate.key-pattern= # Pattern that tells the aggregator what to do with the keys from the source repository.spring.metrics.export.aggregate.prefix= # Prefix for global repository if active.spring.metrics.export.delay-millis=5000 # Delay in milliseconds between export ticks. Metrics are exported to external sources on a schedule with this delay.spring.metrics.export.enabled=true # Flag to enable metric export (assuming a MetricWriter is available).spring.metrics.export.excludes= # List of patterns for metric names to exclude. Applied after the includes.spring.metrics.export.includes= # List of patterns for metric names to include.spring.metrics.export.redis.key=keys.spring.metrics # Key for redis repository export (if active).spring.metrics.export.redis.prefix=spring.metrics # Prefix for redis repository if active.spring.metrics.export.send-latest= # Flag to switch off any available optimizations based on not exporting unchanged metric values.spring.metrics.export.statsd.host= # Host of a statsd server to receive exported metrics.spring.metrics.export.statsd.port=8125 # Port of a statsd server to receive exported metrics.spring.metrics.export.statsd.prefix= # Prefix for statsd exported metrics.spring.metrics.export.triggers.*= # Specific trigger properties per MetricWriter bean name.

自动配置的 HealthIndicators

在目录org.springframework.boot.actuate.health下面,Spring Boot 默认自动配了如下的HealthIndicators

  • CassandraHealthIndicator

检查 Cassandra database is up.

  • DiskSpaceHealthIndicator

检查 low disk space.

  • DataSourceHealthIndicator

检查数据库连接是否正常

  • ElasticsearchHealthIndicator

检查Elasticsearch cluster is up.

  • JmsHealthIndicator

检查JMS broker is up.

  • MailHealthIndicator

检查mail server is up.

  • MongoHealthIndicator

检查Mongo database is up.

  • RabbitHealthIndicator

检查Rabbit server is up.

  • RedisHealthIndicator

检查Redis server is up.

  • SolrHealthIndicator

检查Solr server is up.

举例说明,比如DataSourceHealthIndicator,其健康检查代码如下

        private void doDataSourceHealthCheck(Health.Builder builder) throws Exception {        String product = getProduct();        builder.up().withDetail("database", product);        String validationQuery = getValidationQuery(product);        if (StringUtils.hasText(validationQuery)) {            try {                // Avoid calling getObject as it breaks MySQL on Java 7                List<Object> results = this.jdbcTemplate.query(validationQuery,                        new SingleColumnRowMapper());                Object result = DataAccessUtils.requiredSingleResult(results);                builder.withDetail("hello", result);            }            catch (Exception ex) {                builder.down(ex);            }        }    }

实现逻辑很简单,就是执行一次query,看返回是否正常。

自定义 HealthIndicators

我们要自定义一个HealthIndicators,只需要注册一个Spring bean,实现HealthIndicator接口,实现其health()方法,返回一个 Health 对象即可。

我们可以直接参考Spring Boot 默认自动配置的那些HealthIndicators,仿照着写即可。

一个简单的示例代码如下:

import org.springframework.boot.actuate.health.Health;import org.springframework.boot.actuate.health.HealthIndicator;import org.springframework.stereotype.Component;@Componentpublic class MyHealthIndicator implements HealthIndicator {    @Override    public Health health() {        int errorCode = check(); // perform some specific health check        if (errorCode != 0) {            return Health.down().withDetail("Error Code", errorCode).build();        }        return Health.up().build();    }}

不过有一点需要说明的是,Actuator 的这些HTTP Endpoints是基于 Spring MVC的应用的。

写一个自己的Endpoint

只需要实现Endpoint<T> 接口即可。这个接口的定义如下:

package org.springframework.boot.actuate.endpoint;public interface Endpoint<T> {    String getId();    boolean isEnabled();    boolean isSensitive();    T invoke();}

其中,getId()方法返回的就是url的path。

我们来实现一个获取当前运行服务器的基本信息的Endpoint,代码如下:

package com.springboot.in.action.actuatorimport java.net.InetAddressimport java.utilimport org.springframework.boot.actuate.endpoint.Endpointimport org.springframework.stereotype.Component/**  * Created by jack on 2017/5/3.  */@Componentclass ServerEndpoint extends Endpoint[java.util.List[String]] {  override def invoke(): java.util.List[String] = {    val serverDetails = new util.ArrayList[String]    try {      serverDetails.add("Server IP Address : " + InetAddress.getLocalHost.getHostAddress)      serverDetails.add("Server OS : " + System.getProperty("os.name").toLowerCase)    } catch {      case e: Exception =>        e.printStackTrace()    }    serverDetails  }  override def getId: String = "server"  override def isSensitive: Boolean = false  override def isEnabled: Boolean = true}

重新部署运行,访问 http://localhost:58888/actuator/server ,我们看到输出如下:

// 20170503235224// http://localhost:58888/actuator/server[  "Server IP Address : 192.168.1.104",  "Server OS : mac os x"]

11.2 Spring Boot远程Shell

Spring Boot通过集成Java shell框架CRaSH,让我们可以使用ssh或telnet命令连接到运行的应用。

添加以下依赖即可启用远程shell支持:

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-remote-shell</artifactId> </dependency>

如果想使用telnet访问,需要另外添加org.crsh:crsh.shell.telnet的依赖。

配置完毕,重启应用。我们会看到启动日志

01:00:42.611 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property auth.spring.roles=ADMIN from properties 01:00:42.619 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=SSHPlugin,interface=SSHPlugin] 01:00:42.621 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=SSHInlinePlugin,interface=CommandPlugin] 01:00:42.623 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=KeyAuthenticationPlugin,interface=KeyAuthenticationPlugin] 01:00:42.648 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=CronPlugin,interface=CronPlugin] 01:00:42.657 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=MailPlugin,interface=MailPlugin] 01:00:42.661 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=CRaSHShellFactory,interface=ShellFactory] 01:00:42.663 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=GroovyLanguageProxy,interface=Language] 01:00:42.680 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=JavaLanguage,interface=Language] 01:00:42.687 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=ScriptLanguage,interface=Language] 01:00:42.700 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=JaasAuthenticationPlugin,interface=AuthenticationPlugin] 01:00:42.730 [main] INFO  org.crsh.plugin.PluginManager - Loaded plugin Plugin[type=SimpleAuthenticationPlugin,interface=AuthenticationPlugin] 01:00:42.739 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property ssh.port=2000 from properties 01:00:42.739 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property ssh.auth_timeout=600000 from properties 01:00:42.740 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property ssh.idle_timeout=600000 from properties 01:00:42.741 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property ssh.default_encoding=UTF-8 from properties 01:00:42.742 [main] INFO  o.s.b.a.a.CrshAutoConfiguration$CrshBootstrapBean - Configuring property auth=spring from properties 01:00:42.766 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=KeyAuthenticationPlugin,interface=KeyAuthenticationPlugin] 01:00:42.766 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=JaasAuthenticationPlugin,interface=AuthenticationPlugin] 01:00:42.766 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=SimpleAuthenticationPlugin,interface=AuthenticationPlugin] 01:00:42.766 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=AuthenticationManagerAdapter,interface=AuthenticationPlugin] 01:00:42.767 [main] INFO  org.crsh.ssh.SSHPlugin - Booting SSHD 01:00:42.793 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=GroovyLanguageProxy,interface=Language] 01:00:42.861 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=JavaLanguage,interface=Language] 01:00:42.861 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=ScriptLanguage,interface=Language] 01:00:42.871 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=CRaSHShellFactory,interface=ShellFactory] 01:00:44.185 [main] INFO  o.a.sshd.common.util.SecurityUtils - Trying to register BouncyCastle as a JCE provider 01:00:45.739 [main] INFO  o.a.sshd.common.util.SecurityUtils - Registration succeeded 01:00:45.978 [main] INFO  org.crsh.ssh.term.SSHLifeCycle - About to start CRaSSHD 01:00:46.070 [main] INFO  org.crsh.ssh.term.SSHLifeCycle - CRaSSHD started on port 2000 01:00:46.070 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=SSHPlugin,interface=SSHPlugin] 01:00:46.070 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=SSHInlinePlugin,interface=CommandPlugin] 01:00:46.071 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=CronPlugin,interface=CronPlugin] 01:00:46.071 [main] INFO  org.crsh.plugin.PluginManager - Initialized plugin Plugin[type=MailPlugin,interface=MailPlugin]

从CRaSSHD started on port 2000这句看出,ssh默认监听的端口号为2000。SpringBoot的关于remote shell的属性配置,在org.springframework.boot.actuate.autoconfigure.ShellProperties类里面。

我们也可以在application.properties里面自行配置端口号:

management.shell.ssh.port=2001

执行如下命令

$ ssh -p 2000 user@localhost

我们就可以在shell终端连接我们的应用了。

如果我们什么都不配置,使用默认的配置,远程shell默认监听端口为2000,默认用户名为user,密码为随机生成的,并且在输出日志中会显示。

如果我们应用集成了Spring Security,remote shell使用默认系统的配置。

management.shell.auth.spring.roles=ADMIN,USER#spring, Integrated with spring securitymanagement.shell.auth.type=spring

这样配置将使用Spring Security的AuthenticationManager处理登录职责,相关代码如下:

private static class AuthenticationManagerAdapter extends CRaSHPlugin<AuthenticationPlugin> implements AuthenticationPlugin<String> {        private static final PropertyDescriptor<String> ROLES = PropertyDescriptor.create("auth.spring.roles", "ADMIN", "Comma separated list of roles required to access the shell");        @Autowired        private AuthenticationManager authenticationManager;        @Autowired(            required = false        )        @Qualifier("shellAccessDecisionManager")        private AccessDecisionManager accessDecisionManager;        private String[] roles;        private AuthenticationManagerAdapter() {            this.roles = new String[]{"ADMIN"};        }        public boolean authenticate(String username, String password) throws Exception {            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);            Authentication token;            try {                token = this.authenticationManager.authenticate(token);            } catch (AuthenticationException var6) {                return false;            }            if(this.accessDecisionManager != null && token.isAuthenticated() && this.roles != null) {                try {                    this.accessDecisionManager.decide(token, this, SecurityConfig.createList(this.roles));                } catch (AccessDeniedException var5) {                    return false;                }            }            return token.isAuthenticated();        }

具体参考CrshAutoConfiguration和ShellProperties的Javadoc。

如果不是,将使用一个简单的认证策略,你可能会看到类似这样的信息:

Using default password for shell access: ec03e16c-4cf4-49ee-b745-7c8255c1dd7e

如果想在简单的认证策略里指定用户名密码,按照如下配置即可

management.shell.auth.type=simplemanagement.shell.auth.simple.user.name=usermanagement.shell.auth.simple.user.password=123456

这个安全机制就是用的Spring security里面的功能。

Linux和OSX用户可以直接使用ssh连接远程shell,Windows用户可以下载并安装PuTTY,SecureCRT等客户端来实现ssh连接。

输入help可以获取命令列表,Spring Boot提供metrics,beans,autoconfig和endpoint命令。如下所示

jack@jacks-MacBook-Air:~$ ssh -p 2000 user@localhostPassword authenticationPassword:   .   ____          _            __ _ _ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )  '  |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot ::        (v1.4.6.RELEASE)> helpTry one of these commands with the -h or --help switch:                       NAME       DESCRIPTION                                                        autoconfig Display auto configuration report from ApplicationContext          beans      Display beans in ApplicationContext                                cron       manages the cron plugin                                            dashboard  a monitoring dashboard                                             egrep      search file(s) for lines that match a pattern                      endpoint   Invoke actuator endpoints                                          env        display the term env                                               filter     a filter for a stream of map                                       java       various java language commands                                     jmx        Java Management Extensions                                         jul        java.util.logging commands                                         jvm        JVM informations                                                   less       opposite of more                                                   mail       interact with emails                                               man        format and display the on-line manual pages                        metrics    Display metrics provided by Spring Boot                            shell      shell related command                                              sleep      sleep for some time                                                sort       sort a map                                                         system     vm system properties commands                                      thread     JVM thread commands                                                help       provides basic help                                                repl       list the repl or change the current repl

比如,我们想看一下系统有那些Endpoint,直接命令行操作如下

> endpointusage: endpoint [-h | --help] COMMAND [ARGS]The most commonly used endpoint commands are:   invoke           Invoke provided actuator endpoint   list             List all available and enabled actuator endpoints> endpoint listserverEndpointrequestMappingEndpointenvironmentEndpointhealthEndpointbeansEndpointinfoEndpointmetricsEndpointtraceEndpointdumpEndpointautoConfigurationReportEndpointconfigurationPropertiesReportEndpoint

连上ssh终端,我们看应用端的后台打印的日志

02:06:04.657 [main] INFO  o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat started on port(s): 8888 (http) 02:06:04.673 [main] INFO  scala.App - Started App in 52.422 seconds (JVM running for 56.978) 02:06:13.105 [pool-6-thread-1] INFO  o.a.s.server.session.ServerSession - Server session created from /127.0.0.1:50538 02:06:13.260 [pool-6-thread-1] INFO  o.a.s.server.session.ServerSession - Kex: server->client aes128-ctr hmac-sha2-256 none 02:06:13.260 [pool-6-thread-1] INFO  o.a.s.server.session.ServerSession - Kex: client->server aes128-ctr hmac-sha2-256 none 02:06:16.370 [pool-6-thread-1] INFO  o.a.s.s.s.ServerUserAuthService - Session user@/127.0.0.1:50538 authenticated

可以看出,ServerUserAuthService负责认证当前登录用户,ServerSession负责维护当前登录session。

你可以使用Groovy或Java编写其他的shell命令(具体参考CRaSH文档),Spring Boot默认会搜索以下路径的命令:

classpath*:/commands/**classpath*:/crash/commands/**

如果我们想改变搜索路径,设置shell.command-path-patterns属性即可。

下面是一个从src/main/resources/commands/hello.java加载的'hello'命令:

/** * Created by jack on 2017/5/4. */package commands;import java.text.SimpleDateFormat;import java.util.Date;import org.crsh.cli.Command;import org.crsh.cli.Usage;import org.crsh.command.BaseCommand;import org.crsh.command.InvocationContext;import org.springframework.beans.factory.support.DefaultListableBeanFactory;@Usage("Test Command : hello say")public class hello extends BaseCommand{    @Usage("hello say")    @Command    public String say(InvocationContext context){        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) context.getAttributes().get("spring.beanfactory");        for (String name : defaultListableBeanFactory.getBeanDefinitionNames()) {            System.out.println(name);            context.getWriter().write(name);            context.getWriter().write("\n");        }        return "Hello, Now Is " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());    }}

在远程ssh中执行命令,结果如下

jack@jacks-MacBook-Air:~$ ssh -p 2000 user@localhostPassword authenticationPassword:   .   ____          _            __ _ _ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )  '  |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot ::        (v1.4.6.RELEASE)> helpTry one of these commands with the -h or --help switch:                                                                               NAME       DESCRIPTION                                                                                                                autoconfig Display auto configuration report from ApplicationContext                                                                  beans      Display beans in ApplicationContext                                                                                        cron       manages the cron plugin                                                                                                    dashboard  a monitoring dashboard                                                                                                     egrep      search file(s) for lines that match a pattern                                                                              endpoint   Invoke actuator endpoints                                                                                                  env        display the term env                                                                                                       filter     a filter for a stream of map                                                                                               hello      Test Command : hello say                                                                                                   java       various java language commands                                                                                             jmx        Java Management Extensions                                                                                                 jul        java.util.logging commands                                                                                                 jvm        JVM informations                                                                                                           less       opposite of more                                                                                                           mail       interact with emails                                                                                                       man        format and display the on-line manual pages                                                                                metrics    Display metrics provided by Spring Boot                                                                                    shell      shell related command                                                                                                      sleep      sleep for some time                                                                                                        sort       sort a map                                                                                                                 system     vm system properties commands                                                                                              thread     JVM thread commands                                                                                                        help       provides basic help                                                                                                        repl       list the repl or change the current repl                                                                                   > hellousage: hello [-h | --help] COMMAND [ARGS]The most commonly used hello commands are:   say              hello say> hello say......lightSwordHealthIndicatorserverEndpointglobalExceptionHandlerAdviceangularControllerhelloControllerhttpApiControllerhttpReportControllerhttpSuiteControllerrootControllerwebMvcConfigwebSecurityConfigdataInitlightSwordUserDetailServiceorg.springframework.security.config.annotation.authentication.configuration.AuthenticationConfigurationauthenticationManagerBuilderenableGlobalAuthenticationAutowiredConfigurerinitializeUserDetailsBeanManagerConfigurerinitializeAuthenticationProviderBeanManagerConfigurerorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurationdelegatingApplicationListenerwebSecurityExpressionHandlerspringSecurityFilterChain........org.springframework.boot.autoconfigure.web.MultipartPropertiesorg.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfigurationrestTemplateBuilderorg.springframework.boot.autoconfigure.web.WebClientAutoConfigurationorg.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$DatabaseShutdownExecutorJpaDependencyConfigurationorg.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfigurationinMemoryDatabaseShutdownExecutorloginFilterorg.springframework.orm.jpa.SharedEntityManagerCreator#0Hello, Now Is 2017-05-04 02:29:38

除了创建新命令,你也可以扩展CRaSH shell的其他特性,所有继承org.crsh.plugin.CRaSHPlugin的Spring Beans将自动注册到shell,具体可以查看CRaSH参考文档[4]。

小结

本章示例工程源代码:

https://github.com/EasySpringBoot/lightsword/tree/spring_boot_actuator_and_remote_shell_2017.5.4

参考资料:
1.http://javabeat.net/spring-boot-actuator/
2.http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-remote-shell.html
3.https://my.oschina.net/zj0303/blog/708366
4.http://www.crashub.org/