天天看點

第11章 Spring Boot應用監控

第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候選者及它們被應用或未被應用的原因 true
GET /configprops 檢視配置屬性,包括預設配置, 顯示一個所有@ConfigurationProperties的整理清單 true
GET /beans bean及其關系清單, 顯示一個應用中所有Spring Beans的完整清單 true
GET /dump 列印線程棧 true
GET /env 檢視所有環境變量 true
GET /env/{name} 檢視具體變量值 true
GET /health 檢視應用健康名額, 當使用一個未認證連接配接通路時顯示一個簡單的’status’,使用認證連接配接通路則顯示全部資訊詳情 false
GET /info 檢視應用資訊 false
GET /mappings 檢視所有url映射, 即所有@RequestMapping路徑的整理清單 true
GET /metrics 檢視應用基本名額 true
GET /metrics/{name} 檢視具體名額 true
POST /shutdown 關閉應用,允許應用以優雅的方式關閉(預設情況下不啟用) true
GET /trace 檢視基本追蹤資訊,預設為最新的一些HTTP請求 true

這些HTTP端點(Endpoint),預設是系統根路徑通路的。如果我們想自定義context-path, 按照如下配置即可:

server.port=8888

#actuator
management.port=58888
management.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 endpoints
management.cloudfoundry.skip-ssl-validation= # Skip SSL verification for Cloud Foundry actuator endpoint security calls
management.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 INDICATORS
management.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;

@Component
public 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.actuator

import java.net.InetAddress
import java.util

import org.springframework.boot.actuate.endpoint.Endpoint
import org.springframework.stereotype.Component

/**
  * Created by jack on 2017/5/3.
  */
@Component
class 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 security
management.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=simple
management.shell.auth.simple.user.name=user
management.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@localhost
Password authentication
Password: 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.6.RELEASE)

> help
Try 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,直接指令行操作如下

> endpoint
usage: 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 list
serverEndpoint
requestMappingEndpoint
environmentEndpoint
healthEndpoint
beansEndpoint
infoEndpoint
metricsEndpoint
traceEndpoint
dumpEndpoint
autoConfigurationReportEndpoint
configurationPropertiesReportEndpoint      

連上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@localhost
Password authentication
Password: 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.6.RELEASE)

> help
Try 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                                                                                   

> hello
usage: hello [-h | --help] COMMAND [ARGS]

The most commonly used hello commands are:
   say              hello say



> hello say
......

lightSwordHealthIndicator
serverEndpoint
globalExceptionHandlerAdvice
angularController
helloController
httpApiController
httpReportController
httpSuiteController
rootController
webMvcConfig
webSecurityConfig
dataInit
lightSwordUserDetailService
org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
authenticationManagerBuilder
enableGlobalAuthenticationAutowiredConfigurer
initializeUserDetailsBeanManagerConfigurer
initializeAuthenticationProviderBeanManagerConfigurer
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration
delegatingApplicationListener
webSecurityExpressionHandler
springSecurityFilterChain
........

org.springframework.boot.autoconfigure.web.MultipartProperties
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration
restTemplateBuilder
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration
org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$DatabaseShutdownExecutorJpaDependencyConfiguration
org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration
inMemoryDatabaseShutdownExecutor
loginFilter
org.springframework.orm.jpa.SharedEntityManagerCreator#0
Hello, Now Is 2017-05-04 02:29:38      

除了建立新指令,你也可以擴充CRaSH shell的其他特性,所有繼承org.crsh.plugin.CRaSHPlugin的Spring Beans将自動注冊到shell,具體可以檢視CRaSH參考文檔[4]。

小結