diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties index baa1dba33..71862fe26 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties @@ -1,6 +1,8 @@ spring.application.name=sentinel-example server.port=18083 management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always + spring.cloud.sentinel.transport.dashboard=localhost:8080 spring.cloud.sentinel.eager=true @@ -19,4 +21,4 @@ spring.cloud.sentinel.datasource.ds4.file.file=classpath: system.json spring.cloud.sentinel.datasource.ds4.file.rule-type=system spring.cloud.sentinel.datasource.ds5.file.file=classpath: param-flow.json -spring.cloud.sentinel.datasource.ds5.file.rule-type=param-flow +spring.cloud.sentinel.datasource.ds5.file.rule-type=param_flow diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java index 7c9b5d1c5..7388147a9 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.cloud.alibaba.sentinel.endpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; +import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -38,4 +39,10 @@ public class SentinelEndpointAutoConfiguration { return new SentinelEndpoint(sentinelProperties); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledHealthIndicator("sentinel") + public SentinelHealthIndicator sentinelHealthIndicator(SentinelProperties sentinelProperties) { + return new SentinelHealthIndicator(sentinelProperties); + } } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.java new file mode 100644 index 000000000..821021006 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.java @@ -0,0 +1,67 @@ +package org.springframework.cloud.alibaba.sentinel.endpoint; + +import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider; +import com.alibaba.csp.sentinel.transport.HeartbeatSender; +import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@link HealthIndicator} for Sentinel. + *

+ * Check the status of Sentinel Dashboard by sending a heartbeat message to it. + * + * Note: if sentinel isn't enabled or sentinel-dashboard isn't configured, + * the health status is up and more infos are provided in detail. + *

+ * + * @author cdfive + */ +public class SentinelHealthIndicator extends AbstractHealthIndicator { + + private SentinelProperties sentinelProperties; + + public SentinelHealthIndicator(SentinelProperties sentinelProperties) { + this.sentinelProperties = sentinelProperties; + } + + @Override + protected void doHealthCheck(Health.Builder builder) throws Exception { + Map detailMap = new HashMap<>(); + + // If sentinel isn't enabled, set the status up and set the enabled to false in detail + if (!sentinelProperties.isEnabled()) { + detailMap.put("enabled", false); + builder.up().withDetails(detailMap); + return; + } + + detailMap.put("enabled", true); + + String consoleServer = TransportConfig.getConsoleServer(); + // If dashboard isn't configured, set the status to UNKNOWN + if (StringUtils.isEmpty(consoleServer)) { + detailMap.put("dashboard", new Status(Status.UNKNOWN.getCode(), "dashboard isn't configured")); + builder.up().withDetails(detailMap); + return; + } + + // If dashboard is configured, send a heartbeat message to it and check the result + HeartbeatSender heartbeatSender = HeartbeatSenderProvider.getHeartbeatSender(); + boolean result = heartbeatSender.sendHeartbeat(); + if (result) { + detailMap.put("dashboard", Status.UP); + builder.up().withDetails(detailMap); + } else { + detailMap.put("dashboard", new Status(Status.DOWN.getCode(), consoleServer + " can't be connected")); + builder.down().withDetails(detailMap); + } + } +} diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicatorTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicatorTests.java new file mode 100644 index 000000000..7baaf2054 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicatorTests.java @@ -0,0 +1,90 @@ +package org.springframework.cloud.alibaba.sentinel.endpoint; + +import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider; +import com.alibaba.csp.sentinel.transport.HeartbeatSender; +import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test cases for {@link SentinelHealthIndicator}. + * + * @author cdfive + */ +public class SentinelHealthIndicatorTests { + + private SentinelHealthIndicator sentinelHealthIndicator; + + private SentinelProperties sentinelProperties; + + private HeartbeatSender heartbeatSender; + + @Before + public void setUp() { + sentinelProperties = mock(SentinelProperties.class); + sentinelHealthIndicator = new SentinelHealthIndicator(sentinelProperties); + + SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, ""); + + heartbeatSender = mock(HeartbeatSender.class); + Field heartbeatSenderField = ReflectionUtils.findField(HeartbeatSenderProvider.class, "heartbeatSender"); + heartbeatSenderField.setAccessible(true); + ReflectionUtils.setField(heartbeatSenderField, null, heartbeatSender); + } + + @Test + public void testSentinelNotEnabled() { + when(sentinelProperties.isEnabled()).thenReturn(false); + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails().get("enabled")).isEqualTo(false); + } + + @Test + public void testSentinelDashboardNotConfigured() { + when(sentinelProperties.isEnabled()).thenReturn(true); + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails().get("dashboard")).isEqualTo(Status.UNKNOWN); + } + + @Test + public void testSentinelDashboardConfiguredCheckSuccess() throws Exception { + when(sentinelProperties.isEnabled()).thenReturn(true); + SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080"); + when(heartbeatSender.sendHeartbeat()).thenReturn(true); + + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.UP); + } + + @Test + public void testSentinelDashboardConfiguredCheckFailed() throws Exception { + when(sentinelProperties.isEnabled()).thenReturn(true); + SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080"); + when(heartbeatSender.sendHeartbeat()).thenReturn(false); + + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails().get("dashboard")).isEqualTo(new Status(Status.DOWN.getCode(), "localhost:8080 can't be connected")); + } +}