Переглянути джерело

Add Spring Cloud Config data source extension (#899)

master
Lin.Liang Eric Zhao 5 роки тому
джерело
коміт
30158bcac0
16 змінених файлів з 897 додано та 0 видалено
  1. +1
    -0
      sentinel-extension/pom.xml
  2. +48
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/README.md
  3. +88
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/pom.xml
  4. +294
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SentinelRuleLocator.java
  5. +44
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SentinelRuleStorage.java
  6. +109
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SpringCloudConfigDataSource.java
  7. +48
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/config/DataSourceBootstrapConfiguration.java
  8. +2
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/resources/META-INF/spring.factories
  9. +32
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SimpleSpringApplication.java
  10. +34
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/client/ConfigClient.java
  11. +35
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/server/ConfigServer.java
  12. +71
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/test/SentinelRuleLocatorTests.java
  13. +74
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/test/SpringCouldDataSourceTest.java
  14. +10
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/bootstrap.yml
  15. +3
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/config-client-application.properties
  16. +4
    -0
      sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/config-server-application.properties

+ 1
- 0
sentinel-extension/pom.xml Переглянути файл

@@ -19,6 +19,7 @@
<module>sentinel-datasource-redis</module>
<module>sentinel-annotation-aspectj</module>
<module>sentinel-parameter-flow-control</module>
<module>sentinel-datasource-spring-cloud-config</module>
<module>sentinel-datasource-consul</module>
</modules>



+ 48
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/README.md Переглянути файл

@@ -0,0 +1,48 @@
# Sentinel DataSource SpringCloudConfig

Sentinel DataSource SpringCloudConfig provides integration with SpringCloudConfig so that SpringCloudConfig
can be the dynamic rule data source of Sentinel.

To use Sentinel DataSource SpringCloudConfig, 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
//flow_rule is the propery key in SpringConfigConfig
SpringCloudConfigDataSource dataSource = new SpringCloudConfigDataSource("flow_rule", new Converter<String, List<FlowRule>>() {
@Override
public List<FlowRule> convert(String source) {
return JSON.parseArray(source, FlowRule.class);
}
});
FlowRuleManager.register2Property(dataSource.getProperty());
```

If the client want to perceive the remote config changed, it can binding a git webhook callback with the ```com.alibaba.csp.sentinel.datasource.spring.cloud.config.SentinelRuleLocator.refresh```
API. Like test demo ```com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SpringCouldDataSourceTest.refresh``` do.


We offer test cases and demo in:
[com.alibaba.csp.sentinel.datasource.spring.cloud.config.test].
When you run test cases, please follow the steps:

```
//first start config server
com.alibaba.csp.sentinel.datasource.spring.cloud.config.server.ConfigServer

//second start config client
com.alibaba.csp.sentinel.datasource.spring.cloud.config.client.ConfigClient

//third run test cases and demo
com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SentinelRuleLocatorTests
com.alibaba.csp.sentinel.datasource.spring.cloud.config.test.SpringCouldDataSourceTest
```

+ 88
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/pom.xml Переглянути файл

@@ -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.7.0-SNAPSHOT</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>

+ 294
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SentinelRuleLocator.java Переглянути файл

@@ -0,0 +1,294 @@
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 pull sentinel rules from remote server.
* It retrieve configurations of spring-cloud-config client configurations from {@link org.springframework.core.env.Environment}
* Such as spring.cloud.config.uri=uri, spring.cloud.config.profile=profile .... and so on.
* When pull rules successfully, save to {@link SentinelRuleStorage} for ${@link SpringCloudConfigDataSource} retrieve.
* </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(String.format(
"Located environment: name=%s, profiles=%s, label=%s, version=%s, state=%s",
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(String.format(
"Environment %s has %d property sources with %d 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.info("Multiple Config Server Urls found listed.");
}

RecordLog.info("properties = {0},label={1}, state={2}", 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("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.info("Connect Timeout Exception on Url - " + uri
+ ". Will be trying the next url if available");
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;
}

}
}

+ 44
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SentinelRuleStorage.java Переглянути файл

@@ -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();
}

}

+ 109
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SpringCloudConfigDataSource.java Переглянути файл

@@ -0,0 +1,109 @@
/*
* 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;

/**
* ${@link SpringCloudConfigDataSource} A read-only {@code DataSource} with spring-cloud-config backend
* It retrieve the spring-cloud-config data stored in ${@link SentinelRuleStorage}
* When the data in backend has been modified, ${@link SentinelRuleStorage} will invoke ${@link SpringCloudConfigDataSource#updateValues()}
* to dynamic update values
*
* @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 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);
}
}

}
}


+ 48
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/config/DataSourceBootstrapConfiguration.java Переглянути файл

@@ -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;
}


}

+ 2
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/main/resources/META-INF/spring.factories Переглянути файл

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

+ 32
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/SimpleSpringApplication.java Переглянути файл

@@ -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);
}

}

+ 34
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/client/ConfigClient.java Переглянути файл

@@ -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);
}
}

+ 35
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/server/ConfigServer.java Переглянути файл

@@ -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);

}
}

+ 71
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/test/SentinelRuleLocatorTests.java Переглянути файл

@@ -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")));

}

}

+ 74
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/java/com/alibaba/csp/sentinel/datasource/spring/cloud/config/test/SpringCouldDataSourceTest.java Переглянути файл

@@ -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();
}
}

+ 10
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/bootstrap.yml Переглянути файл

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



+ 3
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/config-client-application.properties Переглянути файл

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


+ 4
- 0
sentinel-extension/sentinel-datasource-spring-cloud-config/src/test/resources/config-server-application.properties Переглянути файл

@@ -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

Завантаження…
Відмінити
Зберегти