From 9c638b8d3f31f47494ebf9de0ee11e69f517fb0a Mon Sep 17 00:00:00 2001 From: cdfive <176402936@qq.com> Date: Sun, 26 May 2019 15:46:09 +0800 Subject: [PATCH 1/4] Add SentinelHealthIndicator to do some health check for Sentinel #265 --- .../src/main/resources/application.properties | 4 +- .../SentinelEndpointAutoConfiguration.java | 7 ++ .../endpoint/SentinelHealthIndicator.java | 67 ++++++++++++++ .../SentinelHealthIndicatorTests.java | 90 +++++++++++++++++++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.java create mode 100644 spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicatorTests.java 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 baa1dba3..71862fe2 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 7c9b5d1c..7388147a 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 00000000..82102100 --- /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 00000000..7baaf205 --- /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")); + } +} From 3e2fa1b903e24f1e2456158ec4185cbeb296a108 Mon Sep 17 00:00:00 2001 From: cdfive <176402936@qq.com> Date: Sun, 26 May 2019 15:46:39 +0800 Subject: [PATCH 2/4] Fix typo in md files, acutator-> actuator,implmentation->implementation --- spring-cloud-alibaba-examples/oss-example/readme-zh.md | 2 +- spring-cloud-alibaba-examples/oss-example/readme.md | 4 ++-- .../sentinel-example/sentinel-core-example/readme-zh.md | 2 +- spring-cloud-alibaba-examples/sms-example/readme-zh.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-cloud-alibaba-examples/oss-example/readme-zh.md b/spring-cloud-alibaba-examples/oss-example/readme-zh.md index 54cc88d2..bc491e1b 100644 --- a/spring-cloud-alibaba-examples/oss-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/oss-example/readme-zh.md @@ -128,7 +128,7 @@ Spring Boot 应用支持通过 Endpoint 来暴露相关信息,OSS Starter 也 Spring Boot1.x 可以通过访问 http://127.0.0.1:18084/oss 来查看 OSS Endpoint 的信息。 -Spring Boot2.x 可以通过访问 http://127.0.0.1:18084/acutator/oss 来访问。 +Spring Boot2.x 可以通过访问 http://127.0.0.1:18084/actuator/oss 来访问。 Endpoint 内部会显示所有的 OSSClient 配置信息,以及该 OSSClient 对应的 Bucket 列表。 diff --git a/spring-cloud-alibaba-examples/oss-example/readme.md b/spring-cloud-alibaba-examples/oss-example/readme.md index 11ea77c8..3e5fe291 100644 --- a/spring-cloud-alibaba-examples/oss-example/readme.md +++ b/spring-cloud-alibaba-examples/oss-example/readme.md @@ -114,7 +114,7 @@ You can verify results on the OSS console when you finish uploading or downloadi ## Endpoint -OSS starter also supports the implmentation of Spring Boot acutator endpoints. +OSS starter also supports the implementation of Spring Boot actuator endpoints. **Prerequisite:** @@ -127,7 +127,7 @@ To view the endpoint information, visit the following URLs: Spring Boot1.x: OSS Endpoint URL is http://127.0.0.1:18084/oss. -Spring Boot2.x: OSS Endpoint URL is http://127.0.0.1:18084/acutator/oss. +Spring Boot2.x: OSS Endpoint URL is http://127.0.0.1:18084/actuator/oss. Endpoint will show the configurations and the list of buckets of all OSSClients. diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md index bfdc599b..8b3a9214 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md @@ -184,7 +184,7 @@ Spring Boot 应用支持通过 Endpoint 来暴露相关信息,Sentinel Starter * Spring Boot 1.x 中添加配置 `management.security.enabled=false` * Spring Boot 2.x 中添加配置 `management.endpoints.web.exposure.include=*` -Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/acutator/sentinel 来访问。 +Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/sentinel 来访问。

diff --git a/spring-cloud-alibaba-examples/sms-example/readme-zh.md b/spring-cloud-alibaba-examples/sms-example/readme-zh.md index b21875c9..c07b3608 100644 --- a/spring-cloud-alibaba-examples/sms-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sms-example/readme-zh.md @@ -306,7 +306,7 @@ Spring Boot 应用支持通过 Endpoint 来暴露相关信息,SMS Starter 也 Spring Boot1.x 可以通过访问 http://127.0.0.1:18084/sms-info 来查看 SMS Endpoint 的信息。 -Spring Boot2.x 可以通过访问 http://127.0.0.1:18084/acutator/sms-info 来访问。 +Spring Boot2.x 可以通过访问 http://127.0.0.1:18084/actuator/sms-info 来访问。 Endpoint 内部会显示最近 20 条单个短信发送的记录和批量短信发送的记录,以及当前短信消息的配置信息(包括是**SmsReport** 还是 **SmsUp**,**队列名称**,以及对应的 **MessageListener** )。 From 584d5f41075d0711f8e6e8736dbd643c535fde04 Mon Sep 17 00:00:00 2001 From: cdfive <176402936@qq.com> Date: Tue, 28 May 2019 21:53:41 +0800 Subject: [PATCH 3/4] Check the health status of Sentinel DataSource using AbstractDataSource#loadConfig. --- .../src/main/resources/application.properties | 4 + .../SentinelEndpointAutoConfiguration.java | 5 +- .../endpoint/SentinelHealthIndicator.java | 86 ++++++++++++++++--- ...itional-spring-configuration-metadata.json | 6 ++ .../SentinelHealthIndicatorTests.java | 80 ++++++++++++++++- 5 files changed, 162 insertions(+), 19 deletions(-) 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 71862fe2..c3d0959d 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 @@ -3,6 +3,10 @@ server.port=18083 management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always +# we can disable health check, default is enable +management.health.diskspace.enabled=false +# management.health.sentinel.enabled=false + spring.cloud.sentinel.transport.dashboard=localhost:8080 spring.cloud.sentinel.eager=true 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 7388147a..9af7be97 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 @@ -16,6 +16,7 @@ package org.springframework.cloud.alibaba.sentinel.endpoint; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; 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; @@ -42,7 +43,7 @@ public class SentinelEndpointAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledHealthIndicator("sentinel") - public SentinelHealthIndicator sentinelHealthIndicator(SentinelProperties sentinelProperties) { - return new SentinelHealthIndicator(sentinelProperties); + public SentinelHealthIndicator sentinelHealthIndicator(DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties) { + return new SentinelHealthIndicator(beanFactory, 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 index 82102100..765a3909 100644 --- 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 @@ -1,8 +1,26 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.alibaba.sentinel.endpoint; +import com.alibaba.csp.sentinel.datasource.AbstractDataSource; 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.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; @@ -14,21 +32,36 @@ import java.util.HashMap; import java.util.Map; /** - * {@link HealthIndicator} for Sentinel. + * A {@link HealthIndicator} for Sentinel, which checks the status of + * Sentinel Dashboard and DataSource. + * *

* Check the status of Sentinel Dashboard by sending a heartbeat message to it. + * If no Exception thrown, it's OK. * - * Note: if sentinel isn't enabled or sentinel-dashboard isn't configured, - * the health status is up and more infos are provided in detail. + * Check the status of Sentinel DataSource by calling loadConfig method of {@link AbstractDataSource}. + * If return true, it's OK. + * + * If Dashboard and DataSource are both OK, the health status is UP. + *

+ * + *

+ * Note: + * If Sentinel isn't enabled, the health status is up. + * If Sentinel Dashboard isn't configured, it's OK and mark the status of Dashboard with UNKNOWN. + * More informations are provided in details. *

* * @author cdfive */ public class SentinelHealthIndicator extends AbstractHealthIndicator { + private DefaultListableBeanFactory beanFactory; + private SentinelProperties sentinelProperties; - public SentinelHealthIndicator(SentinelProperties sentinelProperties) { + public SentinelHealthIndicator(DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties) { + this.beanFactory = beanFactory; this.sentinelProperties = sentinelProperties; } @@ -45,22 +78,49 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { detailMap.put("enabled", true); + // Check health of Dashboard + boolean dashboardUp = true; String consoleServer = TransportConfig.getConsoleServer(); - // If dashboard isn't configured, set the status to UNKNOWN if (StringUtils.isEmpty(consoleServer)) { + // If Dashboard isn't configured, mark the status of Dashboard with UNKNOWN and the dashboardUp is still true detailMap.put("dashboard", new Status(Status.UNKNOWN.getCode(), "dashboard isn't configured")); - builder.up().withDetails(detailMap); - return; + } else { + // 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); + } else { + // If failed to send heartbeat message, means that the Dashboard is DOWN + dashboardUp = false; + detailMap.put("dashboard", new Status(Status.DOWN.getCode(), consoleServer + " can't be connected")); + } } - // 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); + // Check health of DataSource + boolean dataSourceUp = true; + Map dataSourceDetailMap = new HashMap<>(); + detailMap.put("dataSource", dataSourceDetailMap); + + // Get all DataSources and each call loadConfig to check if it's OK + Map dataSourceMap = beanFactory.getBeansOfType(AbstractDataSource.class); + for (Map.Entry dataSourceMapEntry : dataSourceMap.entrySet()) { + String dataSourceBeanName = dataSourceMapEntry.getKey(); + AbstractDataSource dataSource = dataSourceMapEntry.getValue(); + try { + dataSource.loadConfig(); + dataSourceDetailMap.put(dataSourceBeanName, Status.UP); + } catch (Exception e) { + // If one DataSource failed to loadConfig, means that the DataSource is DOWN + dataSourceUp = false; + dataSourceDetailMap.put(dataSourceBeanName, new Status(Status.DOWN.getCode(), e.getMessage())); + } + } + + // If Dashboard and DataSource are both OK, the health status is UP + if (dashboardUp && dataSourceUp) { 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/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 21713de7..44b0b459 100644 --- a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -88,6 +88,12 @@ "type": "java.lang.String", "defaultValue": "3", "description": "sentinel the cold factor." + }, + { + "name": "management.health.sentinel.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable sentinel health check.", + "defaultValue": true } ] } 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 index 7baaf205..6245c4a1 100644 --- 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 @@ -1,17 +1,38 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.alibaba.sentinel.endpoint; import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.datasource.AbstractDataSource; +import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource; 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.beans.factory.support.DefaultListableBeanFactory; 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 java.util.HashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -26,14 +47,17 @@ public class SentinelHealthIndicatorTests { private SentinelHealthIndicator sentinelHealthIndicator; + private DefaultListableBeanFactory beanFactory; + private SentinelProperties sentinelProperties; private HeartbeatSender heartbeatSender; @Before public void setUp() { + beanFactory = mock(DefaultListableBeanFactory.class); sentinelProperties = mock(SentinelProperties.class); - sentinelHealthIndicator = new SentinelHealthIndicator(sentinelProperties); + sentinelHealthIndicator = new SentinelHealthIndicator(beanFactory, sentinelProperties); SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, ""); @@ -64,19 +88,18 @@ public class SentinelHealthIndicatorTests { } @Test - public void testSentinelDashboardConfiguredCheckSuccess() throws Exception { + public void testSentinelDashboardConfiguredSuccess() 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 { + public void testSentinelDashboardConfiguredFailed() throws Exception { when(sentinelProperties.isEnabled()).thenReturn(true); SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080"); when(heartbeatSender.sendHeartbeat()).thenReturn(false); @@ -87,4 +110,53 @@ public class SentinelHealthIndicatorTests { assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat(health.getDetails().get("dashboard")).isEqualTo(new Status(Status.DOWN.getCode(), "localhost:8080 can't be connected")); } + + @Test + public void testSentinelDataSourceSuccess() throws Exception { + when(sentinelProperties.isEnabled()).thenReturn(true); + SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080"); + when(heartbeatSender.sendHeartbeat()).thenReturn(true); + + Map dataSourceMap = new HashMap<>(); + + FileRefreshableDataSource fileDataSource1 = mock(FileRefreshableDataSource.class); + dataSourceMap.put("ds1-sentinel-file-datasource", fileDataSource1); + + FileRefreshableDataSource fileDataSource2 = mock(FileRefreshableDataSource.class); + dataSourceMap.put("ds2-sentinel-file-datasource", fileDataSource2); + + when(beanFactory.getBeansOfType(AbstractDataSource.class)).thenReturn(dataSourceMap); + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.UP); + Map dataSourceDetailMap = (Map) health.getDetails().get("dataSource"); + assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource")).isEqualTo(Status.UP); + assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource")).isEqualTo(Status.UP); + } + + @Test + public void testSentinelDataSourceFailed() throws Exception { + when(sentinelProperties.isEnabled()).thenReturn(true); + SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080"); + when(heartbeatSender.sendHeartbeat()).thenReturn(true); + + Map dataSourceMap = new HashMap<>(); + + FileRefreshableDataSource fileDataSource1 = mock(FileRefreshableDataSource.class); + dataSourceMap.put("ds1-sentinel-file-datasource", fileDataSource1); + + FileRefreshableDataSource fileDataSource2 = mock(FileRefreshableDataSource.class); + when(fileDataSource2.loadConfig()).thenThrow(new RuntimeException("fileDataSource2 error")); + dataSourceMap.put("ds2-sentinel-file-datasource", fileDataSource2); + + when(beanFactory.getBeansOfType(AbstractDataSource.class)).thenReturn(dataSourceMap); + + Health health = sentinelHealthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + Map dataSourceDetailMap = (Map) health.getDetails().get("dataSource"); + assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource")).isEqualTo(Status.UP); + assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource")).isEqualTo(new Status(Status.DOWN.getCode(), "fileDataSource2 error")); + } } From a0a4bb050797ac729985841cf44a67a2d70ae054 Mon Sep 17 00:00:00 2001 From: cdfive <176402936@qq.com> Date: Wed, 29 May 2019 12:38:27 +0800 Subject: [PATCH 4/4] Fix error comment and add note comment for checking dataSource. --- .../sentinel/endpoint/SentinelHealthIndicator.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 index 765a3909..8ca863ab 100644 --- 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 @@ -37,10 +37,10 @@ import java.util.Map; * *

* Check the status of Sentinel Dashboard by sending a heartbeat message to it. - * If no Exception thrown, it's OK. + * If return true, it's OK. * * Check the status of Sentinel DataSource by calling loadConfig method of {@link AbstractDataSource}. - * If return true, it's OK. + * If no Exception thrown, it's OK. * * If Dashboard and DataSource are both OK, the health status is UP. *

@@ -82,7 +82,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { boolean dashboardUp = true; String consoleServer = TransportConfig.getConsoleServer(); if (StringUtils.isEmpty(consoleServer)) { - // If Dashboard isn't configured, mark the status of Dashboard with UNKNOWN and the dashboardUp is still true + // If Dashboard isn't configured, it's OK and mark the status of Dashboard with UNKNOWN. detailMap.put("dashboard", new Status(Status.UNKNOWN.getCode(), "dashboard isn't configured")); } else { // If Dashboard is configured, send a heartbeat message to it and check the result @@ -103,6 +103,11 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { detailMap.put("dataSource", dataSourceDetailMap); // Get all DataSources and each call loadConfig to check if it's OK + // If no Exception thrown, it's OK + // Note: + // Even if the dynamic config center is down, the loadConfig() might return successfully + // e.g. for Nacos client, it might retrieve from the local cache) + // But in most circumstances it's okay Map dataSourceMap = beanFactory.getBeansOfType(AbstractDataSource.class); for (Map.Entry dataSourceMapEntry : dataSourceMap.entrySet()) { String dataSourceBeanName = dataSourceMapEntry.getKey();