mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
sync sentinel healthindicator and log check in finchley
This commit is contained in:
parent
7229bc8615
commit
9ccb1d5de4
@ -18,11 +18,9 @@ package org.springframework.cloud.alibaba.sentinel.custom;
|
|||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -35,13 +33,11 @@ import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePr
|
|||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
||||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||||
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sentinel {@link ReadableDataSource} Handler Handle the configurations of
|
* Sentinel {@link ReadableDataSource} Handler Handle the configurations of
|
||||||
@ -210,61 +206,8 @@ public class SentinelDataSourceHandler implements SmartInitializingSingleton {
|
|||||||
AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
|
AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
|
||||||
.getBean(dataSourceName);
|
.getBean(dataSourceName);
|
||||||
|
|
||||||
logAndCheckRuleType(newDataSource, dataSourceName,
|
|
||||||
dataSourceProperties.getRuleType().getClazz());
|
|
||||||
|
|
||||||
// register property in RuleManager
|
// register property in RuleManager
|
||||||
dataSourceProperties.postRegister(newDataSource);
|
dataSourceProperties.postRegister(newDataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
|
|
||||||
Class<? extends AbstractRule> ruleClass) {
|
|
||||||
Object ruleConfig;
|
|
||||||
try {
|
|
||||||
ruleConfig = dataSource.loadConfig();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
|
||||||
+ " loadConfig error: " + e.getMessage(), e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ruleConfig instanceof List || ruleConfig instanceof Set) {
|
|
||||||
Collection convertedRuleList = (Collection) ruleConfig;
|
|
||||||
if (CollectionUtils.isEmpty(convertedRuleList)) {
|
|
||||||
log.warn("[Sentinel Starter] DataSource {} rule list is empty.",
|
|
||||||
dataSourceName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int matchCount = 0;
|
|
||||||
for (Object rule : convertedRuleList) {
|
|
||||||
if (rule.getClass() == ruleClass) {
|
|
||||||
matchCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchCount == 0) {
|
|
||||||
log.error("[Sentinel Starter] DataSource {} none rules are {} type.",
|
|
||||||
dataSourceName, ruleClass.getSimpleName());
|
|
||||||
throw new IllegalArgumentException("[Sentinel Starter] DataSource "
|
|
||||||
+ dataSourceName + " none rules are " + ruleClass.getSimpleName()
|
|
||||||
+ " type.");
|
|
||||||
}
|
|
||||||
else if (matchCount != convertedRuleList.size()) {
|
|
||||||
log.warn("[Sentinel Starter] DataSource {} all rules are not {} type.",
|
|
||||||
dataSourceName, ruleClass.getSimpleName());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.info("[Sentinel Starter] DataSource {} load {} {}", dataSourceName,
|
|
||||||
convertedRuleList.size(), ruleClass.getSimpleName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
|
||||||
+ " rule class is not List<" + ruleClass.getSimpleName()
|
|
||||||
+ ">. Class: " + ruleConfig.getClass());
|
|
||||||
throw new IllegalArgumentException("[Sentinel Starter] DataSource "
|
|
||||||
+ dataSourceName + " rule class is not List<"
|
|
||||||
+ ruleClass.getSimpleName() + ">. Class: " + ruleConfig.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator;
|
||||||
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
|
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
@ -38,4 +40,13 @@ public class SentinelEndpointAutoConfiguration {
|
|||||||
return new SentinelEndpoint(sentinelProperties);
|
return new SentinelEndpoint(sentinelProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
@ConditionalOnEnabledHealthIndicator("sentinel")
|
||||||
|
public SentinelHealthIndicator sentinelHealthIndicator(
|
||||||
|
DefaultListableBeanFactory beanFactory,
|
||||||
|
SentinelProperties sentinelProperties) {
|
||||||
|
return new SentinelHealthIndicator(beanFactory, sentinelProperties);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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<String, Object> 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<String, Object> 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<String, AbstractDataSource> dataSourceMap = beanFactory
|
||||||
|
.getBeansOfType(AbstractDataSource.class);
|
||||||
|
for (Map.Entry<String, AbstractDataSource> 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<String, Object> detailMap) {
|
||||||
|
for (String key : detailMap.keySet()) {
|
||||||
|
builder.withDetail(key, detailMap.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -94,6 +94,12 @@
|
|||||||
"name": "feign.sentinel.enabled",
|
"name": "feign.sentinel.enabled",
|
||||||
"description": "If true, an OpenFeign client will be wrapped with a Sentinel circuit breaker.",
|
"description": "If true, an OpenFeign client will be wrapped with a Sentinel circuit breaker.",
|
||||||
"type": "java.lang.Boolean"
|
"type": "java.lang.Boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "management.health.sentinel.enabled",
|
||||||
|
"type": "java.lang.Boolean",
|
||||||
|
"description": "Whether to enable sentinel health check.",
|
||||||
|
"defaultValue": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user