CLOVERšŸ€

That was when it all began.

Spring Boot Actuatorć§ćƒ‡ćƒ¼ć‚æć‚¹ćƒˆć‚¢ļ¼ˆJDBC态Redisļ¼‰ć‚’å«ć‚“ć ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚’č”Œć†

恓悌ćÆ态ćŖć«ć‚’ć—ćŸćć¦ę›øć„ćŸć‚‚ć®ļ¼Ÿ

Spring Boot Actuatorć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚’č”Œć†ę™‚ć«ć€äø€ē·’ć«ćƒ‡ćƒ¼ć‚æć‚¹ćƒˆć‚¢ćŖć©ć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚‚č”Œć£ć¦ćć‚Œć‚‹ć‚ˆć†ćŖ恮恧态
ć”ć‚‡ć£ćØ見恦ćæć¾ć—ćŸć€‚

Spring Boot Actuatorļ¼Health Information

恝悂恝悂态Spring Boot ActuatorćÆļ¼ŸćØ恄恆ćØ恓悍恋悉恧恙恌怂

Spring Boot ActuatorćÆć€ęœ¬ē•Ŗē’°å¢ƒć§ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’å‹•ä½œć•ć›ć‚‹éš›ć«ćƒ¢ćƒ‹ć‚æćƒŖćƒ³ć‚°ć€ē®”ē†ć™ć‚‹ć®ć«ä¾æ利ćŖę©Ÿčƒ½ć‚’å«ć‚“ć ć‚‚ć®ć§ć™ć€‚
HTTPć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆć¾ćŸćÆJMX悒ä½æē”Øć—ć¦ć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ē®”ē†ć—ćŸć‚Šćƒ¢ćƒ‹ć‚æćƒŖćƒ³ć‚°ć—ćŸć‚Šć§ćć¾ć™ć€‚ē›£ęŸ»ć€ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ态
惔惈ćƒŖć‚Æć‚¹ć®åŽé›†ćŖć©ć‚’å«ć‚“ć§ć„ć¾ć™ć€‚

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

Production-ready Features

Spring Boot Actuator恫ćÆ态複ꕰ恮ć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćØå‘¼ć°ć‚Œć‚‹ć‚‚ć®ćŒć‚ć‚Šć¾ć™ć€‚ćć®äø€č¦§ćÆ态仄äø‹ć«čØ˜č¼‰ć—ć¦ć‚ć‚Šć¾ć™ć€‚

Production-ready Features / Endpoints

ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć®å “åˆćÆ态healthć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćŒč©²å½“ć—ć¾ć™ć€‚ä»„é™ćÆćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æļ¼ˆhealthć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆļ¼‰ć«ēµžć£ć¦ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚’
č¦‹ć¦ć„ćć¾ć™ć€‚

Spring Boot Actuatorć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć«é–¢ć™ć‚‹ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆćÆ态恓恔悉怂

Production-ready Features / Endpoints / Health Information

ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ恫ä½æć‚ć‚Œć‚‹ęƒ…å ±ćÆ态HealthContributorRegistryć‹ć‚‰åŽé›†ć•ć‚ŒćŸć‚‚ć®ć«ćŖ悋ćØę›øć‹ć‚Œć¦ć„ć¾ć™ć€‚

Health information is collected from the content of a HealthContributorRegistry (by default, all HealthContributor instances defined in your ApplicationContext).

ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆ态ApplicationContextć§å®šē¾©ć•ć‚Œć¦ć„ć‚‹ć™ć¹ć¦ć®HealthContributorć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćŒå«ć¾ć‚Œć¦ć„ć¾ć™ć€‚

HealthContributorćÆć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ć‚ć‚Šć€HealthIndicatorć¾ćŸćÆCompositeHealthContributor恮恩恔悉恋恫ćŖć‚Šć¾ć™ć€‚
HealthIndicatorćÆ态Statusć‚’å«ć‚€ćƒ˜ćƒ«ć‚¹ęƒ…å ±ć‚’ęä¾›ć™ć‚‹ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ć™ć€‚CompositeHealthContributorćÆ态複ꕰ恮HealthContributors恮
ēµ„ćæåˆć‚ć›ć«ćŖć‚Šć¾ć™ć€‚

A HealthContributor can be either a HealthIndicator or a CompositeHealthContributor. A HealthIndicator provides actual health information, including a Status. A CompositeHealthContributor provides a composite of other HealthContributors.

ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆć€ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć®é †ć«å„HealthIndicatorć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć‚’äø¦ć³ę›æ恈悋恓ćØć«ć‚ˆć‚Šć€ęœ€ēµ‚ēš„ćŖć‚·ć‚¹ćƒ†ćƒ ć®ćƒ˜ćƒ«ć‚¹ęƒ…å ±ćÆ
StatusAggregatorć«ć‚ˆć£ć¦å°Žå‡ŗć•ć‚Œć¾ć™ć€‚

By default, the final system health is derived by a StatusAggregator, which sorts the statuses from each HealthIndicator based on an ordered list of statuses.

ć‚½ćƒ¼ćƒˆć•ć‚ŒćŸēµęžœć€ęœ€åˆć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ćŒå…Øä½“ć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ恮ēµęžœćØćŖć‚Šć¾ć™ć€‚

The first status in the sorted list is used as the overall health status.

StatusćØ恄恆恮ćÆ态恓恔悉恧恙恭怂

Status (Spring Boot 2.6.7 API)

UP态DOWN态OUT_OF_SERVICE态UNKNOWN恮4ć¤ć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ćŒć‚ć‚Šć¾ć™ć€‚

ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ć‚½ćƒ¼ćƒˆé †ćÆ态DOWN ā†’ OUT_OF_SERVICE ā†’ UP ā†’ UNKNOWNć®ć‚ˆć†ć§ć™ć€‚

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java#L44-L49

ć¤ć¾ć‚Šć€ć©ć‚Œć‹ć²ćØ恤恧悂DOWN悒čæ”恙HealthIndicator恌恂悌恰态å…Øä½“ć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ恮ēµęžœćØ恗恦ćÆDOWNćØćŖ悋悏恑恧恙恭怂

恔ćŖćæ恫态StatusAggregator恌ēŸ„ć£ć¦ć„ć‚‹ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć‚’čæ”恙HealthIndicatorćŒć²ćØ恤悂ćŖć„å “åˆć€å…Øä½“ć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ćØ恗恦ćÆUNKNOWN恫
ćŖ悋悈恆恧恙怂

If no HealthIndicator returns a status that is known to the StatusAggregator, an UNKNOWN status is used.

ć¾ćŸć€Spring BootćÆć„ćć¤ć‹ć®HealthIndicator悒č‡Ŗå‹•ę§‹ęˆć—ć¾ć™ć€‚ä»„äø‹ć«ćƒŖć‚¹ćƒˆć‚¢ćƒƒćƒ—ć•ć‚Œć¦ć„ć¾ć™ć€‚

Production-ready Features / Endpoints / Health Information / Auto-configured HealthIndicators

ē¾ę™‚ē‚¹ć§ć€ä»„äø‹ćŒåÆ¾č±”ćØćŖ悋悈恆恧恙怂ļ¼ˆļ¼‰å†…ćÆć‚­ćƒ¼åć§ć™ć€‚

  • Apache Cassandraļ¼ˆcassandraļ¼‰
  • Couchbaseļ¼ˆcouchbaseļ¼‰
  • DataSourceļ¼ˆdbļ¼‰
  • ćƒ‡ć‚£ć‚¹ć‚Æļ¼ˆdiskspaceļ¼‰
  • Elasticsearchļ¼ˆelasticsearchļ¼‰
  • Hazelcastļ¼ˆhazelcastļ¼‰
  • InfluxDBļ¼ˆinfluxdbļ¼‰
  • JMSćƒ–ćƒ­ćƒ¼ć‚«ćƒ¼ļ¼ˆjmsļ¼‰
  • LDAPć‚µćƒ¼ćƒćƒ¼ļ¼ˆldapļ¼‰
  • ćƒ”ćƒ¼ćƒ«ć‚µćƒ¼ćƒćƒ¼ļ¼ˆmailļ¼‰
  • MongoDBļ¼ˆmongoļ¼‰
  • Neo4jļ¼ˆneo4jļ¼‰
  • åøø恫UP悒čæ”恙HealthIndicatorļ¼ˆpingļ¼‰
  • RabbitMQļ¼ˆrabbitļ¼‰
  • Redisļ¼ˆredisļ¼‰
  • Apache Solrļ¼ˆsolrļ¼‰

各HealthIndicatorćÆ态management.health.[ć‚­ćƒ¼å].enabledćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć‚’falseć«ęŒ‡å®šć™ć‚‹ćØē„”åŠ¹ć«ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚
ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§č‡Ŗå‹•ę§‹ęˆć•ć‚Œć‚‹HealthIndicatorć‚’ć™ć¹ć¦ē„”åŠ¹ć«ć™ć‚‹å “åˆćÆ态management.health.defaults.enabledćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć‚’
ä½æ恈恰悈恄ćæ恟恄恧恙怂

恔ćŖćæć«ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ęœ‰åŠ¹ć«ćŖć£ć¦ć„ćŖ恄LivenessStateHealthIndicatorļ¼ˆlivenessstateļ¼‰ć€ReadinessStateHealthIndicatorļ¼ˆreadinessstateļ¼‰
ćØ恄恆2恤恮HealthIndicatorć‚‚ć‚ć‚‹ć‚ˆć†ć§ć™ć€‚ć“ć”ć‚‰ćÆ态Kuberneteså‘ć‘ćæ恟恄恧恙恭怂

今回ćÆę‰±ć„ć¾ć›ć‚“ćŒć€Spring Boot Actuator恧ē”Øę„ć•ć‚Œć¦ć„ć‚‹HealthIndicatorć§č¶³ć‚ŠćŖć„å “åˆćÆ态č‡Ŗåˆ†ć§HealthIndicatorć‚’ä½œęˆć™ć‚‹ć“ćØ恫
ćŖ悋悓恧恗悇恆恭怂

Production-ready Features / Endpoints / Health Information / Writing Custom HealthIndicators

ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æå†…ć§ä½æē”Ø恕悌悋HealthIndicatorć®ęƒ…å ±ćÆć€č©³ē“°ęƒ…å ±ćØćŖć‚‹ć®ć§ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆč”Øē¤ŗć•ć‚Œć¾ć›ć‚“ć€‚ć“ć‚Œć‚’č”Øē¤ŗć™ć‚‹ć‚ˆć†ć«
恙悋恫ćÆmanagement.endpoint.health.show-detailsćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć¾ćŸćÆmanagement.endpoint.health.show-componentsćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć‚’
ä½æē”Øć—ć¾ć™ć€‚

The information exposed by the health endpoint depends on the management.endpoint.health.show-details and management.endpoint.health.show-components properties, which can be configured with one of the following values:

Production-ready Features / Endpoints / Health Information

čŖ¬ę˜ŽćÆć“ć‚Œćć‚‰ć„ć«ć—ć¦ć€ä»Šå›žćÆDataSourceļ¼ˆJDBC恧恙恭ļ¼‰ćØRedisć‚’å«ć‚€ć‚ˆć†ć«ę§‹ęˆć—ć¦ć€ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚’č”Œć£ć¦ćæć¾ć—ć‚‡ć†ć€‚

ē’°å¢ƒ

ä»Šå›žć®ē’°å¢ƒćÆ态恓恔悉怂

$ java --version
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-110-generic", arch: "amd64", family: "unix"

ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«ćÆ态MySQL悒ä½æ恆恓ćØć«ć—ć¾ć™ć€‚ćƒćƒ¼ć‚øćƒ§ćƒ³ćÆ仄äø‹ć§ć€172.17.0.2ć§å‹•ä½œć—ć¦ć„ć‚‹ć‚‚ć®ćØć—ć¾ć™ć€‚

$ mysql --version
mysql  Ver 8.0.29 for Linux on x86_64 (MySQL Community Server - GPL)

Redisć®ćƒćƒ¼ć‚øćƒ§ćƒ³ćÆ恓恔悉怂RedisćÆ172.17.0.3ć§å‹•ä½œć—ć¦ć„ć‚‹ć‚‚ć®ćØć—ć¾ć™ć€‚

$ bin/redis-server --version
Redis server v=7.0.0 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=45b2bca311da7733

ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä½œęˆć™ć‚‹

ć¾ćšćÆ态Spring Boot惗惭ć‚ø悧ć‚Æćƒˆć‚’ä½œęˆć—ć¾ć™ć€‚ä¾å­˜é–¢äæ‚恫web态actuator态jdbc态mysql态data-redisć‚’åŠ ćˆć¦ć„ć¾ć™ć€‚

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.6.7 \
  -d javaVersion=17 \
  -d name=actuator-healthcheck-with-datastore \
  -d groupId=org.littlewings \
  -d artifactId=actuator-healthcheck-with-datastore \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.actuator \
  -d dependencies=web,actuator,jdbc,mysql,data-redis \
  -d baseDir=actuator-healthcheck-with-datastore | tar zxvf -

惗惭ć‚ø悧ć‚Æćƒˆå†…ć«ē§»å‹•ć€‚

$ cd actuator-healthcheck-with-datastore

Mavenć®ä¾å­˜é–¢äæ‚ć‚„ćƒ—ćƒ©ć‚°ć‚¤ćƒ³čح定ćŖ恩怂

        <properties>
                <java.version>17</java.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-actuator</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-data-redis</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jdbc</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                </dependency>

                <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <scope>runtime</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

č‡Ŗ動ē”Ÿęˆć•ć‚ŒćŸć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ćÆć€å‰Šé™¤ć—ć¦ćŠćć¾ć™ć€‚

$ rm src/main/java/org/littlewings/spring/actuator/ActuatorHealthcheckWithDatastoreApplication.java src/test/java/org/littlewings/spring/actuator/ActuatorHealthcheckWithDatastoreApplicationTests.java

動作ē¢ŗčŖć®ćŸć‚ć«ć€ē”Øę„ć—ćŸćƒ‡ćƒ¼ć‚æć‚¹ćƒˆć‚¢ć«ć‚¢ć‚Æć‚»ć‚¹ć™ć‚‹Controllerć‚’ä½œęˆć€‚

src/main/java/org/littlewings/spring/actuator/DataStoreController.java

package org.littlewings.spring.actuator;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("datastore")
public class DataStoreController {
    NamedParameterJdbcTemplate jdbcTemplate;
    StringRedisTemplate redisTemplate;

    public DataStoreController(NamedParameterJdbcTemplate jdbcTemplate, StringRedisTemplate redisTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.redisTemplate = redisTemplate;
    }

    @GetMapping("jdbc")
    public String jdbc() {
        return jdbcTemplate
                .queryForObject("select now()", Collections.emptyMap(), LocalDateTime.class)
                .format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"));
    }

    @GetMapping("redis")
    public String redis() {
        return redisTemplate
                .getRequiredConnectionFactory()
                .getConnection()
                .serverCommands()
                .info()
                .getProperty("redis_version");
    }
}

MySQL恫select now()ć‚’ęŠ•ć’ć‚‹ćƒ”ć‚½ćƒƒćƒ‰ćØ态Redisć®ćƒćƒ¼ć‚øćƒ§ćƒ³ć‚’å–å¾—ć™ć‚‹ćƒ”ć‚½ćƒƒćƒ‰ć‚’ä½œęˆć—ć¦ć„ć¾ć™ć€‚

mainć‚Æćƒ©ć‚¹ć€‚

src/main/java/org/littlewings/spring/actuator/App.java

package org.littlewings.spring.actuator;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

čح定ćÆć€ć¾ćšćÆęœ€ä½Žé™ć«ć—ć¦ćŠćć¾ć™ć€‚

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password

spring.redis.url=redis://redispass@172.17.0.3:6379
spring.redis.connect-timeout=3s
spring.redis.timeout=3s

Redis恠恑ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆć‚’å…„ć‚Œć¦ć„ć‚‹ć®ćÆć€ć”ć‚‡ć£ćØē†ē”±ćŒć‚ć‚Šć¾ć™ć€‚

動作ē¢ŗčŖć—ć¦ćŠćć¾ć—ć‚‡ć†ć€‚

ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦ć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’čµ·å‹•ć€‚

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

ē¢ŗčŖć€‚

# MySQL
$ curl localhost:8080/datastore/jdbc
2022-05-14 13:30:15


# Redis
$ curl localhost:8080/datastore/redis
7.0.0

OK恧恙恭怂

Spring Boot Actuatorć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ悒ē¢ŗčŖć—恦ćæ悋

恧ćÆ态Spring Boot Actuatorć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ恮ē¢ŗčŖć«ē§»ć‚Šć¾ć—悇恆怂

ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆ/actuator/health恌Spring Boot Actuatorć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć®ćƒ‘ć‚¹ćØć—ć¦å…¬é–‹ć•ć‚Œć‚‹ć®ć§ć€ć“ć”ć‚‰ć«ć‚¢ć‚Æć‚»ć‚¹ć—ć¦ćæć¾ć™ć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:37:33 GMT

{"status":"UP"}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP"
}

HTTPć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć‚³ćƒ¼ćƒ‰ćÆ200ć§ć€ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ćÆUPćØčæ”ć£ć¦ćć¾ć—ćŸć€‚

恓恓恧态Redisć‚’åœę­¢ć—ć¦ć‚¢ć‚Æć‚»ć‚¹ć—ć¦ćæć¾ć™ć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:38:16 GMT
Connection: close

{"status":"DOWN"}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN"
}

HTTPć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć‚³ćƒ¼ćƒ‰ćŒ503ćØćŖć‚Šć€ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ćÆDOWNćØćŖć‚Šć¾ć—ćŸć€‚

Redisć«ć‚¢ć‚Æć‚»ć‚¹ć§ććŖ恏ćŖć£ćŸć®ć§ć€ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æå…Ø体ćØ恗恦ćÆDOWNćØćŖć£ćŸć‚ˆć†ć§ć™ć€‚

ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³å“ć§ćÆ态恓悓ćŖ惭悰恌å‡ŗåŠ›ć•ć‚Œć¦ć„ć¾ć™ć€‚

2022-05-14 22:38:08.806  INFO 26433 --- [xecutorLoop-1-4] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was /172.17.0.3:6379
2022-05-14 22:38:08.807  WARN 26433 --- [ioEventLoop-4-1] i.l.core.protocol.ConnectionWatchdog     : Cannot reconnect to [172.17.0.3/<unresolved>:6379]: ꎄē¶šć‚’ę‹’å¦ć•ć‚Œć¾ć—ćŸ: /172.17.0.3:6379
2022-05-14 22:38:14.606  INFO 26433 --- [xecutorLoop-1-1] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was 172.17.0.3/<unresolved>:6379
2022-05-14 22:38:14.607  WARN 26433 --- [ioEventLoop-4-3] i.l.core.protocol.ConnectionWatchdog     : Cannot reconnect to [172.17.0.3/<unresolved>:6379]: ꎄē¶šć‚’ę‹’å¦ć•ć‚Œć¾ć—ćŸ: /172.17.0.3:6379

ćØ恓悍恧态Spring Data Redis恮čØ­å®šć§ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆć‚’čØ­å®šć—ć¦ć„ćŸć®ćÆ态ćŖć«ć‚‚ęŒ‡å®šć—ćŖ恄ćØćƒ¬ć‚¹ćƒćƒ³ć‚¹ćŒčæ”ć£ć¦ćć‚‹ć¾ć§ć«ćØ恦悂
Ꙃ間恌恋恋悋恋悉恧恙恭ā€¦ć€‚

恟恠态恓悌恠ćØćƒ¬ć‚¹ćƒćƒ³ć‚¹ć‹ć‚‰ćÆćŖć«ćŒčµ·ć“ć£ć¦ć„ć‚‹ć®ć‹ć‚ć‹ć‚Šć¾ć›ć‚“ć€‚src/main/resources/application.properties恫仄äø‹ć‚’čæ½åŠ ć—恦
ćæć¾ć—ć‚‡ć†ć€‚

management.endpoint.health.show-details=always

ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦ć€å†åŗ¦čµ·å‹•ć€‚

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

Redisć‚‚čµ·å‹•ć—ć¦ćŠćć¾ć™ć€‚

ē¢ŗčŖć™ć‚‹ćØ态恓悓ćŖēµęžœć«ćŖć‚Šć¾ć—ćŸć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:47:55 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"diskSpace":{"status":"UP","details":{"total":100000000000,"free":10000000000,"threshold":10485760,"exists":true}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}

ć”ć‚‡ć£ćØć‚ć‹ć‚Šć„ć‚‰ć„ć®ć§ć€jq恧見恦ćæć¾ć—ć‚‡ć†ć€‚

$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 100000000000,
        "free": 10000000000,
        "threshold": 10485760,
        "exists": true
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

db态diskSpace态ping态redis恮4ć¤ć§ę§‹ęˆć•ć‚Œć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚pingćÆ态åøø恫UP悒čæ”恙HealthIndicator恧恗恟怂

ćƒ‡ć‚£ć‚¹ć‚Æć®ęƒ…å ±ćÆ今回ćÆć‚„ć‚ć¦ćŠćć¾ć—ć‚‡ć†ć€‚ē„”åŠ¹ć«ć—ć¾ć™ć€‚

management.health.diskspace.enabled=false

再åŗ¦ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦čµ·å‹•ć€‚

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

ē¢ŗčŖć€‚ćƒ‡ć‚£ć‚¹ć‚Æć®ęƒ…å ±ćŒćŖ恏ćŖć‚Šć¾ć—ćŸć€‚

$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

恓恓恧态Redisć‚’åœę­¢ć—ć¦ć‚¢ć‚Æć‚»ć‚¹ć—ć¦ćæć¾ć™ć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:52:30 GMT
Connection: close

{"status":"DOWN","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"ping":{"status":"UP"},"redis":{"status":"DOWN","details":{"error":"org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)"}}}}



$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "DOWN",
      "details": {
        "error": "org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)"
      }
    }
  }
}

å„ę§‹ęˆč¦ē“ ć®ć†ć”态Redis恠恑恌DOWN恫ćŖć£ć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚error恫ćÆå¤±ę•—ć—ćŸę™‚ć®ä¾‹å¤–ćƒ”ćƒƒć‚»ćƒ¼ć‚øćŒå…„ć£ć¦ć„ć¾ć™ć­ć€‚

Statusć®ć‚½ćƒ¼ćƒˆé †ćÆ态恓恮ēµęžœć‹ć‚‰ćÆ悏恋悉ćŖ恕恝恆恧恙怂

恓恓恧态恕悉恫Redisć‚‚ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æ恮åÆ¾č±”ć‹ć‚‰å¤–ć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

management.health.redis.enabled=false

ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦čµ·å‹•ć€‚RedisćÆåœę­¢ć—ćŸć¾ć¾ć§ć™ć€‚

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

恙悋ćØć€ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚ÆćŒę­£åøø恫ēµ‚äŗ†ć™ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:55:13 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"ping":{"status":"UP"}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

当ē„¶ć§ć™ćŒć€Redisć«ć‚¢ć‚Æć‚»ć‚¹ć™ć‚‹ę©Ÿčƒ½ć‚’ä½æē”Ø恙悋ćØå¤±ę•—ć—ć¾ć™ć€‚

$ curl localhost:8080/datastore/redis
{"timestamp":"2022-05-14T13:56:27.308+00:00","status":500,"error":"Internal Server Error","path":"/datastore/redis"}

Redisć®ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚ÆćÆć€å†åŗ¦ęœ‰åŠ¹ć«ć—ć¦ćŠćć¾ć—ć‚‡ć†ć€‚

#management.health.redis.enabled=false

恔ćŖćæ恫态MySQLć ć‘ć‚’åœę­¢ć—ćŸå “åˆćÆ恓恆ćŖć‚Šć¾ć—ćŸć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 15:38:57 GMT
Connection: close

{"status":"DOWN","components":{"db":{"status":"DOWN","details":{"error":"org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server."}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN",
  "components": {
    "db": {
      "status": "DOWN",
      "details": {
        "error": "org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server."
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

DataSource态Redisć§č”Œć†ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć®å†…å®¹ć‚’ē¢ŗčŖć—恦ćæ悋

ćØ恓悍恧态DataSourcećØRedis恮HealthIndicatorćÆćŖ恫悒ē¢ŗčŖć—恦恄悋悓恧恗悇恆恭ļ¼Ÿ

ć”ć‚‡ć£ćØ見恦ćæć¾ć—ć‚‡ć†ć€‚

ć¾ćšćÆć€ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć®ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć®å†…å®¹ć‚’ć‚‚ć†1åŗ¦č¦‹ć¦ćæć¾ć™ć€‚

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

dbć®å “åˆćÆvalidationQuery态redisć®å “åˆćÆversionćŒć”ć‚‡ć£ćØäøę€č­°ćŖę„Ÿć˜ćŒć—ć¾ć™ć­ć€‚

dbć«å‰²ć‚Šå½“ć¦ć‚‰ć‚Œć¦ć„ć‚‹DataSourceHealthIndicatorć®å “åˆćÆ态惐ćƒŖćƒ‡ćƒ¼ć‚·ćƒ§ćƒ³ē”Ø恮ć‚Æć‚ØćƒŖćƒ¼ćŒčØ­å®šć•ć‚Œć¦ć„ć‚Œć°ćć”ć‚‰ć‚’ä½æ恄态
恝恆恧ćŖ恑悌恰JDBCćƒ‰ćƒ©ć‚¤ćƒćƒ¼ć®Connection#isValidćƒ”ć‚½ćƒƒćƒ‰ć‚’ä½æ恆悈恆恧恙怂

Connection#isValid(int timeout))

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L107-L118

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L134

ć¤ć¾ć‚Šć€ć“ć®ēŠ¶ę…‹ć®validQueryćØ恄恆恮ćÆConnection#isValidć§ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚’č”Œć£ć¦ć„ć‚‹ć“ćØ悒ē¤ŗć—ć¦ć„ć¾ć™ć€‚

    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L115

redisć«å‰²ć‚Šå½“ć¦ć‚‰ć‚Œć¦ć„ć‚‹RedisHealthIndicatorć®å “åˆćÆ态Redis恌ć‚Æćƒ©ć‚¹ć‚æćƒ¼ę§‹ęˆć‹ć©ć†ć‹ć§å¤‰ć‚ć‚‹ć‚ˆć†ć§ć™ć€‚ä»Šå›žćÆć‚¹ć‚æćƒ³ćƒ‰ć‚¢ćƒ­ćƒ³ć§ć™ć­ć€‚

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java#L58-L64

ć‚¹ć‚æćƒ³ćƒ‰ć‚¢ćƒ­ćƒ³ć®Redisć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®å “åˆćÆ态Redis恮infoć‚³ćƒžćƒ³ćƒ‰ć®redis_versionćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć‚’čæ”ć™ć‚ˆć†ć«ćŖć£ć¦ć„ć¾ć™ć€‚

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealth.java#L37

ćć‚ŒćŒć“ć”ć‚‰ć§ć™ć­ć€‚

    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }

恔ćŖćæ恫态Controllerć§ć‚‚åŒć˜ć“ćØć‚’ć‚„ć£ć¦ć„ć¾ć—ćŸć€‚

    @GetMapping("redis")
    public String redis() {
        return redisTemplate
                .getRequiredConnectionFactory()
                .getConnection()
                .serverCommands()
                .info()
                .getProperty("redis_version");
    }

redis-cli恧ē¢ŗčŖć™ć‚‹ćØ态恓悓ćŖę„Ÿć˜ć«ćŖć‚Šć¾ć™ć€‚

$ bin/redis-cli -a redispass info server | grep redis_version
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
redis_version:7.0.0

ęœ€å¾Œć«ć€DataSourceHealthIndicator恧ä½æ恆validationQuery悒čØ­å®šć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

Spring BootćÆć‚³ćƒć‚Æć‚·ćƒ§ćƒ³ćƒ—ćƒ¼ćƒ«ć«HikariCP悒ä½æć†ć‚ˆć†ć«ćŖć£ć¦ć„ć‚‹ćŸć‚ć€spring.datasource.hikari.connection-test-queryć‚’ęŒ‡å®šć—ć¾ć™ć€‚

spring.datasource.hikari.connection-test-query=select now()

恔ćŖćæ恫态HikariCP恮connectionTestQuery恧恙恌态JDBC version 4ä»„é™ć‚’ä½æć£ć¦ć„ć‚‹å “åˆćÆä½æ悏ćŖ恄恓ćØć‚’å¼·ćęŽØå„Øć•ć‚Œć¦ć„ć¾ć™ć€‚

If your driver supports JDBC4 we strongly recommend not setting this property.

HikariCP / Configuration

Connection#isValidć‚’ć‚µćƒćƒ¼ćƒˆć—ć¦ć„ćŖć„ćƒ¬ć‚¬ć‚·ćƒ¼ćŖē’°å¢ƒå‘恑恮悂恮恧恙怂

This is for "legacy" drivers that do not support the JDBC4 Connection.isValid() API.

ć¤ć¾ć‚Šć€ćµć ć‚“ä½æ恆ꙂćÆspring.datasource.hikari.connection-test-query悒čØ­å®šć—ćŖć„ć‚ˆć†ć«ć—ć¦ćŠćć¾ć—ć‚‡ć†ć€‚

ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦čµ·å‹•ć€‚

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

ē¢ŗčŖć€‚

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 15:47:13 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"select now()","result":"2022-05-14T15:47:13"}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "select now()",
        "result": "2022-05-14T15:47:31"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

validationQueryć®å†…å®¹ćŒå¤‰ć‚ć‚Šć€ć‚Æć‚ØćƒŖćƒ¼ć®ēµęžœć¾ć§å…„ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚

    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "select now()",
        "result": "2022-05-14T15:47:31"
      }
    },

恓悌恧态ē¢ŗčŖOKćØć—ć¾ć—ć‚‡ć†ć€‚

ć¾ćØ悁

Spring Boot Actuatorć§ć€ćƒ‡ćƒ¼ć‚æć‚¹ćƒˆć‚¢ć¾ć§å«ć‚“ć ćƒ˜ćƒ«ć‚¹ćƒć‚§ćƒƒć‚Æć‚’č”Œć£ć¦ćæć¾ć—ćŸć€‚

ćŖ悓ćØćŖćć“ć†ć„ć†ę©Ÿčƒ½ćŒć‚ć‚Šć€ć”ć‚‡ć£ćØčˆˆå‘³ćŒć‚ć£ćŸć®ć§ć“ć®ę©Ÿä¼šć«č©¦ć—ć¦ćŠć„ć¦ć‚ˆć‹ć£ćŸć§ć™ć€‚

ęœ€å¾Œć«ć€application.propertiesć®ć‚³ćƒ”ćƒ³ćƒˆć‚¢ć‚¦ćƒˆć—ć¦ć„ć‚‹å†…å®¹ć‚‚å«ć‚ć¦ć€å…Øä½“ć‚’č²¼ć£ć¦ćŠćć¾ć™ć€‚
ā€» management.health.defaults.enabledćÆć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ęœ‰åŠ¹ćŖAuto Configuration恕悌悋HealthIndicatorć‚’ć™ć¹ć¦ē„”åŠ¹ć«ć™ć‚‹ćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć§ć™

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password
#spring.datasource.hikari.connection-test-query=select now()

spring.redis.url=redis://redispass@172.17.0.3:6379
spring.redis.connect-timeout=3s
spring.redis.timeout=3s

management.endpoint.health.show-details=always

management.health.diskspace.enabled=false
#management.health.db.enabled=false
#management.health.ping.enabled=false
#management.health.redis.enabled=false

#management.health.defaults.enabled=false