This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
# Sentinel DataSource Spring Cloud Config
Sentinel DataSource Spring Cloud Config provides integration with Spring Cloud Config
so that Spring Cloud Config can be the dynamic rule data source of Sentinel.
To use Sentinel DataSource Spring Cloud Config, you should add the following dependency:
```xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-spring-cloud-config</artifactId>
<version>x.y.z</version>
</dependency>
```
Then you can create an `SpringCloudConfigDataSource` and register to rule managers.
For instance:
```Java
ReadableDataSource<String, List<FlowRule>> flowRuleDs = new SpringCloudConfigDataSource<>(ruleKey, s -> JSON.parseArray(s, FlowRule.class));
FlowRuleManager.register2Property(flowRuleDs.getProperty());
```
To notify the client that the remote config has changed, we could bind a git webhook callback with the
`com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleLocator.refresh` API.
We may refer to the the sample `com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SpringCouldDataSourceTest#refresh` in test cases.
We offer test cases and demo in the package: `com.alibaba.csp.sentinel.datasource.spring.cloud.config.test`.
When you are running test cases, please follow the steps:
```
// First, start the Spring Cloud config server
com.alibaba.csp.sentinel.datasource.spring.cloud.config.server.ConfigServer
// Second, start the Spring Cloud config client
com.alibaba.csp.sentinel.datasource.spring.cloud.config.client.ConfigClient
// Third, run the test cases and demo
com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SentinelRuleLocatorTests
com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SpringCouldDataSourceTest
```

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-extension</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.8.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sentinel-datasource-spring-cloud-config</artifactId>
<packaging>jar</packaging>
<properties>
<spring.cloud.version>2.0.0.RELEASE</spring.cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>${spring.cloud.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.cloud.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>${spring.cloud.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.cloud.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,301 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config;
import com.alibaba.csp.sentinel.log.RecordLog;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigClientStateHolder;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.retry.annotation.Retryable;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.cloud.config.client.ConfigClientProperties.*;
/**
* <p>
* {@link SentinelRuleLocator} which pulls Sentinel rules from remote server.
* It retrieves configurations of spring-cloud-config client configurations from
* {@link org.springframework.core.env.Environment}, such as {@code spring.cloud.config.uri=uri},
* {@code spring.cloud.config.profile=profile}, and so on.
* When rules are pulled successfully, it will be stored to {@link SentinelRuleStorage}.
* </p>
*
* @author lianglin
* @since 1.7.0
*/
@Order(0)
public class SentinelRuleLocator implements PropertySourceLocator {
private RestTemplate restTemplate;
private ConfigClientProperties defaultProperties;
private org.springframework.core.env.Environment environment;
public SentinelRuleLocator(ConfigClientProperties defaultProperties,
org.springframework.core.env.Environment environment) {
this.defaultProperties = defaultProperties;
this.environment = environment;
}
/**
* Responsible for pull data from remote server
*
* @param environment
* @return correct data if success else a empty propertySource or null
*/
@Override
@Retryable(interceptor = "configServerRetryInterceptor")
public org.springframework.core.env.PropertySource<?> locate(
org.springframework.core.env.Environment environment) {
ConfigClientProperties properties = this.defaultProperties.override(environment);
CompositePropertySource composite = new CompositePropertySource("configService");
RestTemplate restTemplate = this.restTemplate == null
? getSecureRestTemplate(properties)
: this.restTemplate;
Exception error = null;
String errorBody = null;
try {
String[] labels = new String[] {""};
if (StringUtils.hasText(properties.getLabel())) {
labels = StringUtils
.commaDelimitedListToStringArray(properties.getLabel());
}
String state = ConfigClientStateHolder.getState();
// Try all the labels until one works
for (String label : labels) {
Environment result = getRemoteEnvironment(restTemplate, properties,
label.trim(), state);
if (result != null) {
log(result);
// result.getPropertySources() can be null if using xml
if (result.getPropertySources() != null) {
for (PropertySource source : result.getPropertySources()) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>)source
.getSource();
composite.addPropertySource(
new MapPropertySource(source.getName(), map));
}
}
SentinelRuleStorage.setRulesSource(composite);
return composite;
}
}
} catch (HttpServerErrorException e) {
error = e;
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders().getContentType())) {
errorBody = e.getResponseBodyAsString();
}
} catch (Exception e) {
error = e;
}
if (properties.isFailFast()) {
throw new IllegalStateException(
"Could not locate PropertySource and the fail fast property is set, failing",
error);
}
RecordLog.warn("Could not locate PropertySource: " + (errorBody == null
? error == null ? "label not found" : error.getMessage()
: errorBody));
return null;
}
public org.springframework.core.env.PropertySource<?> refresh() {
return locate(environment);
}
private void log(Environment result) {
RecordLog.info("Located environment: name={}, profiles={}, label={}, version={}, state={}",
result.getName(),
result.getProfiles() == null ? "" : Arrays.asList(result.getProfiles()),
result.getLabel(), result.getVersion(), result.getState());
List<PropertySource> propertySourceList = result.getPropertySources();
if (propertySourceList != null) {
int propertyCount = 0;
for (PropertySource propertySource : propertySourceList) {
propertyCount += propertySource.getSource().size();
}
RecordLog.info("[SentinelRuleLocator] Environment {} has {} property sources with {} properties",
result.getName(), result.getPropertySources().size(), propertyCount);
}
}
private Environment getRemoteEnvironment(RestTemplate restTemplate,
ConfigClientProperties properties, String label, String state) {
String path = "/{name}/{profile}";
String name = properties.getName();
String profile = properties.getProfile();
String token = properties.getToken();
int noOfUrls = properties.getUri().length;
if (noOfUrls > 1) {
RecordLog.debug("[SentinelRuleLocator] Multiple Config Server Urls found listed.");
}
RecordLog.info("[SentinelRuleLocator] getRemoteEnvironment, properties={}, label={}, state={}",
properties, label, state);
Object[] args = new String[] {name, profile};
if (StringUtils.hasText(label)) {
if (label.contains("/")) {
label = label.replace("/", "(_)");
}
args = new String[] {name, profile, label};
path = path + "/{label}";
}
ResponseEntity<Environment> response = null;
for (int i = 0; i < noOfUrls; i++) {
Credentials credentials = properties.getCredentials(i);
String uri = credentials.getUri();
String username = credentials.getUsername();
String password = credentials.getPassword();
RecordLog.info("[SentinelRuleLocator] Fetching config from server at: {}", uri);
try {
HttpHeaders headers = new HttpHeaders();
addAuthorizationToken(properties, headers, username, password);
if (StringUtils.hasText(token)) {
headers.add(TOKEN_HEADER, token);
}
if (StringUtils.hasText(state) && properties.isSendState()) {
headers.add(STATE_HEADER, state);
}
final HttpEntity<Void> entity = new HttpEntity<>((Void)null, headers);
response = restTemplate.exchange(uri + path, HttpMethod.GET, entity,
Environment.class, args);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw e;
}
} catch (ResourceAccessException e) {
RecordLog.warn("[SentinelRuleLocator] ConnectTimeoutException on url <{}>."
+ " Will be trying the next url if available", uri);
if (i == noOfUrls - 1) {
throw e;
} else {
continue;
}
}
if (response == null || response.getStatusCode() != HttpStatus.OK) {
return null;
}
Environment result = response.getBody();
return result;
}
return null;
}
private RestTemplate getSecureRestTemplate(ConfigClientProperties client) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
if (client.getRequestReadTimeout() < 0) {
throw new IllegalStateException("Invalid Value for Read Timeout set.");
}
requestFactory.setReadTimeout(client.getRequestReadTimeout());
RestTemplate template = new RestTemplate(requestFactory);
Map<String, String> headers = new HashMap<>(client.getHeaders());
if (headers.containsKey(AUTHORIZATION)) {
// To avoid redundant addition of header
headers.remove(AUTHORIZATION);
}
if (!headers.isEmpty()) {
template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
new GenericRequestHeaderInterceptor(headers)));
}
return template;
}
private void addAuthorizationToken(ConfigClientProperties configClientProperties,
HttpHeaders httpHeaders, String username, String password) {
String authorization = configClientProperties.getHeaders().get(AUTHORIZATION);
if (password != null && authorization != null) {
throw new IllegalStateException(
"You must set either 'password' or 'authorization'");
}
if (password != null) {
byte[] token = Base64Utils.encode((username + ":" + password).getBytes());
httpHeaders.add("Authorization", "Basic " + new String(token));
} else if (authorization != null) {
httpHeaders.add("Authorization", authorization);
}
}
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public static class GenericRequestHeaderInterceptor
implements ClientHttpRequestInterceptor {
private final Map<String, String> headers;
public GenericRequestHeaderInterceptor(Map<String, String> headers) {
this.headers = headers;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
for (Map.Entry<String, String> header : headers.entrySet()) {
request.getHeaders().add(header.getKey(), header.getValue());
}
return execution.execute(request, body);
}
protected Map<String, String> getHeaders() {
return headers;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config;
import org.springframework.core.env.PropertySource;
/**
* Storage data pull from spring-config-cloud server
* And notice ${@link SpringCloudConfigDataSource} update latest values
*
* @author lianglin
* @since 1.7.0
*/
public class SentinelRuleStorage {
public static PropertySource<?> rulesSource;
public static void setRulesSource(PropertySource<?> source) {
rulesSource = source;
noticeSpringCloudDataSource();
}
public static String retrieveRule(String ruleKey) {
return rulesSource == null ? null : (String) rulesSource.getProperty(ruleKey);
}
private static void noticeSpringCloudDataSource(){
SpringCloudConfigDataSource.updateValues();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config;
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>A read-only {@code DataSource} with Spring Cloud Config backend.</p>
* <p>
* It retrieves the Spring Cloud Config data stored in {@link SentinelRuleStorage}.
* When the data in the backend has been modified, {@link SentinelRuleStorage} will
* invoke {@link SpringCloudConfigDataSource#updateValues()} to update values dynamically.
* </p>
* <p>
* To notify the client that the remote config has changed, users could bind a git
* webhook callback with the {@link SentinelRuleLocator#refresh()} API.
* </p>
*
* @author lianglin
* @since 1.7.0
*/
public class SpringCloudConfigDataSource<T> extends AbstractDataSource<String, T> {
private final static Map<SpringCloudConfigDataSource, SpringConfigListener> listeners;
static {
listeners = new ConcurrentHashMap<>();
}
private final String ruleKey;
public SpringCloudConfigDataSource(final String ruleKey, Converter<String, T> converter) {
super(converter);
if (StringUtil.isBlank(ruleKey)) {
throw new IllegalArgumentException(String.format("Bad argument: ruleKey=[%s]", ruleKey));
}
this.ruleKey = ruleKey;
loadInitialConfig();
initListener();
}
private void loadInitialConfig() {
try {
T newValue = loadConfig();
if (newValue == null) {
RecordLog.warn("[SpringCloudConfigDataSource] WARN: initial application is null, you may have to check your data source");
}
getProperty().updateValue(newValue);
} catch (Exception ex) {
RecordLog.warn("[SpringCloudConfigDataSource] Error when loading initial application", ex);
}
}
private void initListener() {
listeners.put(this, new SpringConfigListener(this));
}
@Override
public String readSource() {
return SentinelRuleStorage.retrieveRule(ruleKey);
}
@Override
public void close() throws Exception {
listeners.remove(this);
}
public static void updateValues() {
for (SpringConfigListener listener : listeners.values()) {
listener.listenChanged();
}
}
private static class SpringConfigListener {
private SpringCloudConfigDataSource dataSource;
public SpringConfigListener(SpringCloudConfigDataSource dataSource) {
this.dataSource = dataSource;
}
public void listenChanged() {
try {
Object newValue = dataSource.loadConfig();
dataSource.getProperty().updateValue(newValue);
} catch (Exception e) {
RecordLog.warn("[SpringConfigListener] load config error: ", e);
}
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config.config;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* <p>
* Define the configuration Loaded when spring application start.
* Put it in META-INF/spring.factories, it will be auto loaded by Spring
* </p>
*
* @author lianglin
* @since 1.7.0
*/
@Configuration
public class DataSourceBootstrapConfiguration {
@Autowired
private ConfigurableEnvironment environment;
@Bean
public SentinelRuleLocator sentinelPropertySourceLocator(ConfigClientProperties properties) {
SentinelRuleLocator locator = new SentinelRuleLocator(
properties, environment);
return locator;
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.csp.sentinel.datasource.spring.cloud.config.config.DataSourceBootstrapConfiguration

View File

@@ -0,0 +1,32 @@
/*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author lianglin
* @since 1.7.0
*/
@SpringBootApplication
public abstract class SimpleSpringApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleSpringApplication.class);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
/**
* @author lianglin
* @since 1.7.0
*/
@SpringBootApplication
@ComponentScan("com.alibaba.csp.sentinel.datasource.spring.cloud.config.test")
@PropertySource("classpath:config-client-application.properties")
public class ConfigClient {
public static void main(String[] args) {
SpringApplication.run(ConfigClient.class);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.context.annotation.PropertySource;
/**
* @author lianglin
* @since 1.7.0
*/
@EnableConfigServer
@SpringBootApplication
@PropertySource("classpath:config-server-application.properties")
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2018-2019 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
*
* https://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 com.alibaba.csp.sentinel.datasource.spring.cloud.config.test;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleLocator;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleStorage;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.config.DataSourceBootstrapConfiguration;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.server.ConfigServer;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author lianglin
* @since 1.7.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DataSourceBootstrapConfiguration.class, properties = {
"spring.application.name=sentinel"
})
public class SentinelRuleLocatorTests {
@Autowired
private SentinelRuleLocator sentinelRulesSourceLocator;
@Autowired
private Environment environment;
@Test
public void testAutoLoad() {
Assert.assertTrue(sentinelRulesSourceLocator != null);
Assert.assertTrue(environment != null);
}
/**
* Before run this test case, please start the Config Server ${@link ConfigServer}
*/
public void testLocate() {
ConfigClientProperties configClientProperties = new ConfigClientProperties(environment);
configClientProperties.setLabel("master");
configClientProperties.setProfile("dev");
configClientProperties.setUri(new String[]{"http://localhost:10086/"});
SentinelRuleLocator sentinelRulesSourceLocator = new SentinelRuleLocator(configClientProperties, environment);
sentinelRulesSourceLocator.locate(environment);
Assert.assertTrue(StringUtil.isNotBlank(SentinelRuleStorage.retrieveRule("flow_rule")));
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.datasource.spring.cloud.config.test;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleLocator;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.SpringCloudConfigDataSource;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.client.ConfigClient;
import com.alibaba.csp.sentinel.datasource.spring.cloud.config.server.ConfigServer;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* Before test, please start ${@link ConfigServer} and ${@link ConfigClient}
*
* @author lianglin
* @since 1.7.0
*/
@RestController
@RequestMapping(value = "/test/dataSource/")
public class SpringCouldDataSourceTest {
@Autowired
private SentinelRuleLocator locator;
Converter<String, List<FlowRule>> converter = new Converter<String, List<FlowRule>>() {
@Override
public List<FlowRule> convert(String source) {
return JSON.parseArray(source, FlowRule.class);
}
};
@GetMapping("/get")
@ResponseBody
public List<FlowRule> get() {
SpringCloudConfigDataSource dataSource = new SpringCloudConfigDataSource("flow_rule", converter);
FlowRuleManager.register2Property(dataSource.getProperty());
return FlowRuleManager.getRules();
}
/**
* WebHook refresh config
*/
@GetMapping("/refresh")
@ResponseBody
public List<FlowRule> refresh() {
locator.refresh();
return FlowRuleManager.getRules();
}
}

View File

@@ -0,0 +1,10 @@
spring:
application:
name: sentinel
cloud:
config:
uri: http://localhost:10086/
profile: dev
label: master

View File

@@ -0,0 +1,3 @@
spring.application.name=sentinel
server.port=8080

View File

@@ -0,0 +1,4 @@
spring.cloud.config.server.git.uri=git@github.com:linlinisme/spring-cloud-config-datasource.git
spring.cloud.config.server.git.search-paths=sentinel
server.port=10086
spring.cloud.config.label=master