Spring Boot application monitoring parsing

In enterprise applications, in addition to understanding the use of Spring Boot service unit test, integration test and other functions, it is also necessary to monitor, operate and maintain various indicators of online applications (such as CPU utilization, memory utilization, normal database connection, user request data, etc.).

In traditional projects, such monitoring, operation and maintenance often need to be realized by other third-party tools. However, spring-Boot-Actuator module is provided in Spring Boot, which can monitor and manage the production environment through HTTP, JMX, SSH, Telnet and other forms. Spring Boot also provides flexible custom interfaces to extend monitoring capabilities.

This chapter does not cover much about the basic use of the ACTUATOR, but focuses on the automatic configuration and implementation principles of the ACTUATOR.

Physical description

The Spring Boot Actuator provides monitoring functions such as Auditing applications, Health status information, and Metrics gathering statistics. In addition, the system provides an extensible Endpoint function for users to customize monitoring indicators. By default, this information is presented in JSON data format and can also be managed by the user interface using the Spring BootAdmin project.

Spring Boot Actuators use the following starter to import.

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

The above starter indirectly introduces the following two dependencies.

<dependency>
<groupId>org. springframework . boot</groupId>
<artifactId>spring- boot- actuator</ artifactId>
</ dependency>
<dependency>
<groupId>org. springfr amework . boot</ groupId>
<artifactId>spring- boot- actuator autoconfigure</ artifactId>
</ dependency>
Copy the code

In the Spring Boot project, Spring-Boot-Actuator as an independent project provides the core functions of the actuator, Spring-boot-bskeleton-autoconfigure provides the function of automatic bskeleton-configuration.

After introducing spring – the boot – starter – physical, by default, and opened the function of monitoring, we can through http://localhost:8080/actuator to check the default support monitoring information.

If you use a browser to access the address, the following information is returned by default.

{_links": "self": {"href": "http://localhost: 8080/actuator", "templated": false}, "health-path": {"href": "http://localhost: 8080/ actuator/health/{*path}", "plated": true ": {"href": "http://localhost :8080/ actuator/ Health ","templated": false}," info": {"href": "http://localhost: 8080/actuator/info", "templated": false } } }Copy the code

As an implicit acknowledgement, the monitoring office for the loopback message is enabled so that users can also disable or enable specific monitoring by configuring it in application.properties.

Enabled =true # Disable all management. Endpoints. Enabled -by-default=false s Enable management  info management . endpoint. info. enabled=trueCopy the code

After understanding the basic integration and configuration of monitoring, the following describes the automatic configuration and mechanism of the Actuator.

Actuator automatic configuration

As for the automatic configuration of the Actuator, we take HealthEndpoint as an example to learn how to configure the Actuator automatically in SpringBoot and obtain the corresponding monitoring information. Also, take a quick look at how the information obtained by these auto-configuration components is presented through URL access.

HealthEndpoint is automatically configured

The automatic configuration of the Actuator is not integrated with Spring Boot Autoconfigure by default, but is implemented through a separate Spring-Boot Bresen-Autoconfigure project. In this project, a lot of automatic configuration was implemented for monitoring different components.

Before continuing with this chapter or upgrading the Spring Boot version, readers should be aware of the versions of Spring BootActuator they use, because from Spring Boot 2.0.x to 2.2.x, The implementation code of the Actuator and its automatic configuration has been changed considerably.

This section to monitory point (Health) related to the automatic configuration of the source code as an example to explain and realization principle, steps and automatic configuration have HealthEndpointAutoConfigurationHealthEndpointConfiguration etc. About the Health related classes are located in the org. Springframework. Boot. Actuate. Autoconfigure. Under the Health package.

The books of the Actuator Actuator configuration are completed in spring.factories under spring-boot-bresen-autoconfigure. HealthEndpointAutoConfiguration is no exception, the source code is as follows.

@Configuration(proxyBeanMethods = false)@ConditionalOnAvailableEndpoint (endpoint = HealthEndpoint . class) @EnableConfigurationProperties @Import({ LegacyHealthEndpointAdaptersConfiguration. class, LegacyHealthEndpoint- CompatibilityConfiguration. class, HealthEndpointConfiguration. class, ReactiveHealthEndpointConfiguration. class, HealthEndpointWebExtens ionConfiguration. class, HealthEndpointReactive- WebExtens ionConfiguration.class }) public class HealthEndpointAutoConfiguration { @Bean @SuppressWarnings("deprecation") HealthEndpointProperties healthEndpointProperties (HealthIndicatorProperti es healthIndicatorPrope rties) { HealthEndpointProperties healthEndpointProperties = new HealthEndpoint - Properties(); healthEndpointProperties . getStatus(). getOrder(). addAll(healthIndicator- Properties . get0r der()); healthEndpointProperties . getStatus(). getHttpMapping() . putAll(healthIn- dicatorPro perties . getHttpMapping()); return healthEndpointProperties; }}Copy the code

@Configuration Automatic Configuration is enabled. @ ConditionalOnAvailableEndpoint for Spring Boot version 2.2 introduced a new annotation, says HealthEndpoint endpoint only can automate the configuration when available. So what situations are available? Available here means that an endpoint must be enabled and exposed.

@ EnableConfigurationProperties opens the health check endpoint, health check indicator attribute configuration,

@import introduces several auto-loading classes, as explained below.

HealthEndpointAutoConfiguration class itself in the Spring before the Boot version 2.1 is no specific code realization, the current version though, added to HealthEndpointProperties configuration class instantiation operations, But it depends on HealthIndicatorProperties parameters has been marked as obsolete, and replaced by HealthEndpointProperties, now not sure whether future versions will have new changes.

In HealthEndpointAutoConfiguration @ Import LegacyHealthEndpointAdaptersConfiguration and LegacyHealthEndpointCompatibilityConf Iguration is an automatic configuration provided for compatibility with older versions, ReactiveHealthEndpointConfiguration and HealthEndpointReactiveWebExtensionConfiguration is for Reactive operation.

Here introduced HealthEndpointConfiguration and HealthEndpointWebExtensionConfiguration.

The first is the base configuration of the /health end point, and the second is the expansion configuration of the pin to the Web. Let’s start with the source code for HealthEndpoint-Configuration.

Configurat ion(proxyBeanMethods = false)d.5) Class HealthEndpointConfi Guratior Health State aggregator @bean@conditionalonmissingBean StatusAggregator healthStatusAggregator(HealthEndpointProperties Properti Es // uses its SimpleStatusAggregator implementation by default, Return new SimpleStatusAggregator(properties.getStatus ().get0rder ()); return SimpleStatusAggregator(properties.getStatus ().get0rder ()); // fCHttpCodeStatusMapper to map Heal TH. To the Http status code of @ Bean @ Condit ionalOnMissingBean HttpCodeStatusMapper healthHttpCodeStatusMapper (HealthEndpointProperties F_ SERVICE to HTTP 503 odestatuSMapper Mapping the Status. The DOWN and the Status. The OUT 0 return new SimpleHttpCodeStatusMapper (properties. The getStatus () getHttpMap ping ()); // CHeal thEndpointGroups, @bear@conditionalonMissingBean HealthEndpointGroups with FHeal thEndpointGroups healthEndpointGroups(ApplicationContext applicationC ontext, HealthEndpointProperties prope rties) {autoConfiguredThendPointGroups The major didn't set its properties primaryroup return new AutoConfiguredHealthEndpointGroups (applicat ionContext, properties); CHeal thContributorRegistry, ConditionalOnMissingBean HealthContributorRegi stry plicationContext with FHeal thContributor's ConditionalOnMissingBean HealthContributorRegi HealthEndpointGroups groups) { Map<String, HealthContributor> healthContributors = new LinkedHashMap<> applicationContext. getBeansOfType(HealthContributor . class)); // If reactor.core. Publ isher. Flux exists in the reactor.core. Moreover, Reactive adaptedreact-ivehealthficolin-IF (ClassUtils. IsPresent (" reactor.core. Publisher.flux ", applicationCont ext. getClassLoader())) { healthContributors. putAll(new AdaptedReactiveHealthContributors(ap- plica tionContext).get()); / / the HealthContributorRegistry implementation class, Pounds of insured registered the registry with the name of the group not to rush the return new AutoConf iguredHealthContributorRegistry (healthContributors groups. GetNames ()); @bear@condit ionalOnMissingBean HealthEndpoint HealthEndpoint (Heal thContr ibutorRegistry registry, HealthE ndpoint Groups groups) { return new HealthEndpoint(registry, groups); Omit the internal AdaptedReactiveHeal Thfunctional coding, use FReactiveHea L thContributor adapter}Copy the code

First of all, HealthEndpointConfiguration annotations in other conditions no annotations, it shows that the class is not automatic Configuration mechanism is used to configure, but once introduced can directly be instantiated @ the Configuration notes.

The Times, The internal main instantiate the class StatusAggregator, HttpCodeStatusMapperHealthEndpoint GroupsHealthContributorRegistry and HealthEndpoint Related base components.

The StatusAggregator interface provides the function of obtaining the specific Status according to the incoming system Status set. In the Status set, UNKNOWN, UP, DOWN, and OUT_ OF_ SERVICE are defined.

The HttpCodeStatusMapper interface provides the Http Status code obtained according to Status, which is used to monitor the mapping between Status and Http Status code. For detailed instantiation operations and functions of other components, you can read the source code with comments in the code, which is not expanded here.

See below HealthEndpointWebExtensionConfiguration, the relatively simple configuration class code, source code is as follows.

@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type . SERVLET) @ConditionalonBean(HealthEndpoint. class) class HealthEndpointWebExtensionConfiguration { @Bean @ConditionalOnBean(HealthEndpoint. class) @ConditionalOnMissingBean HealthEndpointWebExtension healthEndpointWebExtension(HealthContri butorRe g- istry healthContrib utorRegistry, HealthEndpointGroup s groups) { return new HealthEndpointWebExtension(healthContributorRegistry, group s); }}Copy the code

Conditionalon-webapplication specifies that conditionalon-webApplication can be configured only if the application type is Servlet. ConditionalOnBean specifies that this will only take effect if the Bean is stored in HealthEndpoint.

HealthEndpointWebExtensionConfiguration internal initialization HealthEndpointWebExtension to like only, create conditions for the Bean: When an object stored in a Healthendpoint-Webextension object does not exist in the container.

HealthEndpointWebExtension inherited from HealthEndpointSupport, mainly used to provide health endpoint and health endpoint extension base classes.

That’s all we have to say about the auto-configuration class, and you can read the source code for other related functions. In the next section we’ll look at the implementation of the HealthIndicator.

HealthIndicator implementation

In the previous section, we learned about health-related auto-configuration, so how is information about Health obtained? That’s where the HealthIndicator interface comes in.

HealthIndicator interface is a strategy pattern, inherits from the interface HealthContributor. HealthContributor is no specific method defined in the interface, it is Spring Boot version 2.2.0 added in a marker interface, Function on health related information returned by HealthEndpoint. Participants must be HealthIndicator or CompositeHealthContributor.

A getHealth method with a default implementation and an abstract Health method are defined in the HealthIndicator interface, where the getHealth method is also new to Spring Boot 2.2.0.

The HealthIndicator interface source code is as follows.

@functionalInterface Public Interface HealthIndicator extends HealthContributor {// According to the includeDetails, returns the health contributor directive default Health getHealth(boolean includeDetails) { Health health = health(); // Return @health if includeDetails is true. Otherwise, return @health if includeDetails is true. health : health . withoutDetails(); Health health(); }Copy the code

The health method returns the Health object, which stores the application’s health information.

Depending on the includeDetails parameter, the getHealth method decides whether to return the result of the health method directly or a processed Health object with no details.

A common HealthIndicator implementation, Such as JDBC data sources (DataSourceHealthIndicator) disk health indicator (DiskSpaceHealthIndicator), Redis health (RedisHealthIndicator), etc.

How is an example of a HealthIndicator junction built? We take the JDBC data source Health-Indicator as an example. First DataSourceHealthIndicator succeed AbstractHealthIndicator AbstractHealthIndicator and implements HealthIndicator interface, In other words DataSourceHealthIndicator is HealthIndicator type.

DataSourceHealthIndicator instantiation is done through DataSourceHealthContributorAutoConfiguration, related to the source code is as follows.

@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ JdbcTemplate. class, AbstractRoutingDataSource.class })@ConditionalOnBean(DataSource. class) @ConditionalOnEnabledHealthIndicator("db") @AutoConfigureAfter (DataSourceAutoConfiguration. class) public class DataSourceHealthContributorAutoConfiguration extends CompositeHealthContributorConfiguration<AbstractHealthIndicator, DataSource> Implements InitializingBean {Heal thContributor @bean @condit ionalOnMissingBean(name = { "dbHealthIndicator", "dbHea lthContribu tor" }) public HealthContributor dbHealthContributor(Map<String, DataSource> data Sources) {// // Call createContributor of the CompositeHeal thContributorConfigurat ion create Indicator return createContributor(dataSources); // Instantiate the corresponding AbstractHealthIndicator CreateIndicator (DataSource source) {Override protected AbstractHealthIndicator createIndicator(DataSource source) {if (source instanceof AbstractRout ingDataSource) { return new RoutingDataSourceHealthIndicator(); } / / instantiate DataSourceHealthIndicator, parameter data and query return new DataSourceHealthIndicator (source, getValidationQuery(sourc e)); Private String getValidationQuery (DataSource source) {DataSourcePooLMetadata, SQL DataSourcePoolMetadata poolMetadata = this SQL DataSourcePoolMetadata = this  . poolMetadataProvider . getDa- taSourcePoolMetadata(source); return (poolMetadata ! = null) ? poolMetadata. getValidationQuery() : null; }... }Copy the code

Effect on DataSourceHealthContributorAutoConfiguration automatic configuration conditions did not explain in detail, in its internal as you can see, Can be realized through createlndicator DataSourceHealthIndicator the instantiation of the operation. This method is not called directly, but indirectly through the dbHealthContributor method that calls the parent class’s methods.

DataSourceHealthIndicator constructor takes two parameters: a data source object, a query statement. The basic principle of data source health check in this class is to connect the data source to the database and execute the corresponding query statement to verify whether the connection is normal.

Query obtains the DataSourcePoolMetadata object from the data source. If the object is not null, it obtains the DataSourcePoolMetadata object from the getValidationQuery method of the object. The specific implementation of the getValidationQuery method is usually provided by a different DataSource.

Take a – DataSourceHealthIndicator core code.

public class DataSourceHealthIndicator extends AbstractHealthIndicator Q implements InitializingBean { private static final String DEFAULT QUERY = "SELECT 1"; private DataSource dataSource; private String query; private JdbcTemplate jdbcTemplate; @override protected void doHealthCheck(health.builder Builder) throws Exception {if (this.datasource == Null)/if the data source does not exist, @unknown Builder.up ().withdetail (" database", "unknown"); DoDataSourceHealthCheck (Builder); doDataSourceHealthCheck(Builder); Private void doDataSourceHealthCheck(health.Builder Builder) throws Exception // Obtaining the database name. String Product = getProduct(); builder. up() .withDetail("database", product); String validationQuery = getValidationQuery(product); If (stringutls.hastext (validationQuery)) // To avoid breaking MySQL on Java 7, call getObject // execute the SQL statement via JDBC Template List<Object> results = this . jdbcTemplate . query(validat ionQuery , new SingleColumnRowMapper()); Object result = dataAccessutils.requiredSingleresult (results); // The result is stored in the Health builder. withDetail("hello", result); Protected String getValidationQuery(String product) {String auery = this. auery: if (! Stringutils.hastext (query)) {// If the query statement is specified, Get the default sQL statement from the enumeration class DatabaseDriver based on the database name DatabaseDriver specific = databasedriver.fromProductName (product); query = specific . getValidat ionQuery(); if (! Stringutils.hastext (query)) {I/SELECT 1 query= DEFAULT OUERY: query return query; stringutils.hastext (query)) {I/SELECT 1 query= DEFAULT OUERY: query return query; }}Copy the code

DoHealthCheck is the entry method of detection. When the data source exists, the doDataSourceHealthCheck method is called. A query statement will be executed in the doDataSourceHealthCheck method. The results are stored in the Health.Builder.

For SQL statements about queries, if a non-NUL value is passed through the constructor, that value is used; If it is not passed in, it defaults to the one defined in the enumeration class DatabaseDriver; If the enumeration class also is not defined, the default constants defined using DataSourceHealthIndicator DEFAULT_ QUERY value (SELECT1).

Finally, let’s take a look at the names and corresponding driver classes of common databases defined by default in Database River.

Public enum DatabaseDriver {/ Unknown type Unknown (NUL1, nu1l), // Apache Derby. Org.apache.derby.jdbc.embeddeddriver ", "org.apach e. derby.jdbc.embeddedXadatasource ", "SELECT 1 FROM sysibm.sysdummy1 "),  // H2 H2("H2", "org.h2 .Driver", "org.h2. jdbcx. JdbcDataSource", "SELECT 1"), // HyperSQL DataBase. HSQLDB( "HSQL Database Engine", "org. hsq1db. jdbc. JDBCDriver", "org. hsqldb.jdbc . pool. JDBCXADataSource" , "SELECT COUNT(*) FROM INFORMATION_ SCHEMA. SYSTEM_ _USERS"), // sQL Lite SQLITE("SQLite", "org. sqlite .JDBC"), // MySQL MYSQL("MySQL", "com. mysql. cj . jdbc . Driver", "com. mysql . cj . jdbc . MysqlXAData Source", "/* ping */ SELECT 1"), // Oracle. ORACLE("Oracle", "oracle. jdbc . OracleDriver", "oracle. jdbc.xa. client . Oracl eXADataSource", "SELECT 'Hello' from DUAL"), }Copy the code

The above code lists the default definitions for some common databases. The enumeration class DatabaseDriver also defines the default information for other data sources. You can read this class to get the default information for the corresponding database for further study.

After the above deployment, the SQL statement of the corresponding database is obtained, and then the SQL statement is executed by jdbcTemplate to obtain the execution result, and then the requiredSingleResult method of DataAccessUtils is used to verify and obtain the information in the result. Finally, it is stored in Health.

At this point, Health information retrieval is complete, and readers can learn about getting Health information for other components by referring to the _ parsing process above.

Actuator Endpoint Display

In the last section, we learned about automatic configuration of health check and initialization of basic information, so how does it achieve the display of related check result information through URL? In this section, we use Info and Health access to learn the implementation process of the Actuator.

In spring-boot-Actuator, the @endpoint annotation is defined. The @endpoint annotation is used to declare one – actuator endpoints. The annotated classes are declared as endpoints providing application information, which can be exposed using various technologies (including JMX and HTTP).

Many @endpoint classes declare one or more @readOperation, @WriteOperation, and @deleteOperation annotation methods, These methods will automatically adapt to open technologies (JMX, Spring MVC, SpringWebFlux, Jersey, etc.). Custom extensions are also supported.

In the INFO example, the /actuator/ INFO interface is exposed through @endpoint (ID =”info”). The basic format of the access request is as follows.

The core of the http://ip:port/actuator/infoInfoEndpoint code is as follows.

@Endpoint(id = "info") public class InfoEndpoint { @ReadOperationpublic Map<String, Object> info() { Info. Builder builder = new Info . Builder(); for (InfoContributor contributor : this . infoContributors) { contributor . contribute(builder); Info build = builder . build( ) (); return build. getDetails(); }}Copy the code

This class is declared as an INFO Endpoint with the @endpoint (ID =” INFO “) annotation, and walks through the Infopolymorphism values set by the constructor, which then returns the resulting information. By default, access /actuator/info to obtain information.

HealthEndpoint also uses the @endpoint annotation, which internally maps two methods: the Health method and the healthForPath method through the @readOperation annotation.

The source code for HealthEndpoint is as follows.

@Endpoint(id = "health") public class HealthEndpoint extends HealthEndpointSupport<HealthContributr, HealthComponent> private static final String[] EMPTY_ PATH = {}; @ReadOperation public HealthComponent health() { HealthComponent health = health(ApiVersion.V3, EMPTY_ PATH); return (health ! = null) ? health : DEFAULT_ HEALTH; @ReadOperation public HealthComponent healthForPath(@Selector(match = Match. ALL_ REMAIN ING) String... Path) {return health(apiversion.v3, path); }}Copy the code

HealthEndpoint has a larger variation ratio in Spring Boot 2.2.0. The HealthEndpointSupport class is added. And the method returns HealthComponent instead of Health. .

Apiversion.v3 in the above source code addresses the implementation provided by Spring Boot2.2+.

The original constructor of HealthEndpoint, which receives the HealthIndicator argument directly, is also identified as obsolete. It is recommended to use with HealthContributorRegistry and HealthEndpointGroups constructor parameters, at the same time introduces the operations of the group.

What’s special about HealthEndpoint is that when you access /actuator/ Health in debug mode, you can find that the requests do not go to the Health method of HealthEndpoint. This is because the implementation in the HealthEndpointWebExtension based on Web. HealthEndpointWebExtension also inherit HealthEndpointSupport classes, and provides two corresponding @readoperation annotation methods.

EndpointWebExtension(endpoint = Health nEndpoint.class) t. class) public class HealthEndpointWebExtension extends HealthEndpointSupport<Healt Contr HealthComponent> { @ReadOperation public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion, SecurityContext securi tyContext) { return health(apiVersion, securityContext, false, NO_ PATH); @ReadOperation public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion, SecurityContext securi tyContext, @Selector(match = Matc h.ALL REMAINING) String... path) { return health(apiVersion, securityContext, false, path); }... }Copy the code

Taking the health method as an example, the getHealth method of the parent class will be called. After a series of calls and business processing of the parent class, the getStatus method of the parent class will be called. This method returns the status of HealthComponent, which by default is UP when the browser accesses it.

Similarly, if you need to customize the accessible Endpoint, you only need to customize it in the newly created Bean. With the @endpoint annotation, methods in the Bean can be exposed using JMX or HTTP. See the code implemented by SpringBoot for details of how to obtain internal information. In addition, if you want to expose only one form of access, you can use @jMxendpoint or @webendpoint annotations.

At this point, the URL access rendering process for Health information is complete.

summary

This chapter focuses on the automatic configuration and implementation principle of the Actuator. Many other monitoring components are available in the Actuator (see the spring-boot-Skeleton-Autoconfigure registry in Spring. factories). You can analyze them according to the analysis in this chapter. At the same time, you can also try to customize a check indicator for your specific business.

Three things to watch ❤️

If you find this article helpful, I’d like to invite you to do three small favors for me:

  1. Like, forward, have your “like and comment”, is the motivation of my creation.

  2. Follow the public account “Java rotten pigskin” and share original knowledge from time to time.

  3. Also look forward to the follow-up article ing🚀

  4. [666] Scan the code to obtain the learning materials package

Article is not original, and all of us feel while reading the article good remember thumb up forward + attention oh ~ this article source: www.toutiao.com/i6896058510…