From f31f13330e895ebff6776ab003ed1cc2f9fc8e6f Mon Sep 17 00:00:00 2001 From: fangjian0423 Date: Mon, 17 Jun 2019 11:12:55 +0800 Subject: [PATCH] sync sentinel healthindicator in finchley --- .../src/main/resources/application.properties | 3 + .../SentinelEndpointAutoConfiguration.java | 11 ++ .../endpoint/SentinelHealthIndicator.java | 154 ++++++++++++++++++ ...itional-spring-configuration-metadata.json | 6 + 4 files changed, 174 insertions(+) create mode 100644 spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.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..20708bd9 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 @@ -20,3 +20,6 @@ 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 + + +management.endpoint.health.show-details=always \ No newline at end of file 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..132717b6 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,7 +16,9 @@ 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; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -38,4 +40,13 @@ public class SentinelEndpointAutoConfiguration { return new SentinelEndpoint(sentinelProperties); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledHealthIndicator("sentinel") + 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 new file mode 100644 index 00000000..b8fae17b --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelHealthIndicator.java @@ -0,0 +1,154 @@ +/* + * 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 java.util.HashMap; +import java.util.Map; + +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; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.util.StringUtils; + +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; + +/** + * 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 return + * true, it's OK. + * + * Check the status of Sentinel DataSource by calling loadConfig method of + * {@link AbstractDataSource}. If no Exception thrown, 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(DefaultListableBeanFactory beanFactory, + SentinelProperties sentinelProperties) { + this.beanFactory = beanFactory; + 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); + withDetails(builder.up(), detailMap); + return; + } + + detailMap.put("enabled", true); + + // Check health of Dashboard + boolean dashboardUp = true; + String consoleServer = TransportConfig.getConsoleServer(); + if (StringUtils.isEmpty(consoleServer)) { + // 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 + 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")); + } + } + + // 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 + // 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(); + 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) { + withDetails(builder.up(), detailMap); + } + else { + withDetails(builder.down(), detailMap); + } + } + + private void withDetails(Health.Builder builder, Map detailMap) { + for (String key : detailMap.keySet()) { + builder.withDetail(key, detailMap.get(key)); + } + } +} 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 6c43465c..5134a9b6 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 @@ -94,6 +94,12 @@ "name": "feign.sentinel.enabled", "description": "If true, an OpenFeign client will be wrapped with a Sentinel circuit breaker.", "type": "java.lang.Boolean" + }, + { + "name": "management.health.sentinel.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable sentinel health check.", + "defaultValue": true } ] }