Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -1,8 +1,10 @@ | |||
# Sentinel DataSource Etcd | |||
Sentinel DataSource Etcd provides integration with Etcd so that Etcd | |||
Sentinel DataSource Etcd provides integration with etcd so that etcd | |||
can be the dynamic rule data source of Sentinel. The data source uses push model (watcher). | |||
> **NOTE**: This module requires JDK 1.8 or later. | |||
To use Sentinel DataSource Etcd, you should add the following dependency: | |||
```xml | |||
@@ -12,29 +14,26 @@ To use Sentinel DataSource Etcd, you should add the following dependency: | |||
<version>x.y.z</version> | |||
</dependency> | |||
``` | |||
Configure Etcd Connect Properties By Config File (for example sentinel.properties) | |||
We could configure Etcd connection configuration by config file (for example `sentinel.properties`): | |||
``` | |||
csp.sentinel.etcd.end.points=http://ip1:port1,http://ip2:port2 | |||
csp.sentinel.etcd.endpoints=http://ip1:port1,http://ip2:port2 | |||
csp.sentinel.etcd.user=your_user | |||
csp.sentinel.etcd.password=your_password | |||
csp.sentinel.etcd.charset=your_charset | |||
csp.sentinel.etcd.auth.enable=true //if ture open user/password or ssl check | |||
csp.sentinel.etcd.authority=authority //ssl | |||
csp.sentinel.etcd.auth.enable=true # if ture, then open user/password or ssl check | |||
csp.sentinel.etcd.authority=authority # ssl | |||
``` | |||
or JVM args(Add -D prefix) | |||
Or we could configure via JVM -D args or via `SentinelConfig.setConfig(key, value)`. | |||
Then you can create an `EtcdDataSource` and register to rule managers. | |||
For instance: | |||
Then we can create an `EtcdDataSource` and register to rule managers. For instance: | |||
```java | |||
//`rule_key` is the rule config key | |||
ReadableDataSource<String, List<FlowRule>> flowRuleEtcdDataSource = new EtcdDataSource<>(rule_key, (rule) -> JSON.parseArray(rule, FlowRule.class)); | |||
FlowRuleManager.register2Property(flowRuleEtcdDataSource.getProperty()); | |||
// `rule_key` is the rule config key | |||
ReadableDataSource<String, List<FlowRule>> flowRuleEtcdDataSource = new EtcdDataSource<>(rule_key, (rule) -> JSON.parseArray(rule, FlowRule.class)); | |||
FlowRuleManager.register2Property(flowRuleEtcdDataSource.getProperty()); | |||
``` | |||
> Note: It needs to update JDK version to JDK8 | |||
We've also provided an example: [sentinel-demo-etcd-datasource](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-etcd-datasource) |
@@ -19,15 +19,14 @@ import com.alibaba.csp.sentinel.config.SentinelConfig; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
/** | |||
* Configure Etcd Connect Properties | |||
* Etcd connection configuration. | |||
* | |||
* @author lianglin | |||
* @since 1.7.0 | |||
*/ | |||
public final class EtcdConfig { | |||
public final static String END_POINTS = "csp.sentinel.etcd.end.points"; | |||
public final static String END_POINTS = "csp.sentinel.etcd.endpoints"; | |||
public final static String USER = "csp.sentinel.etcd.user"; | |||
public final static String PASSWORD = "csp.sentinel.etcd.password"; | |||
public final static String CHARSET = "csp.sentinel.etcd.charset"; | |||
@@ -36,7 +35,6 @@ public final class EtcdConfig { | |||
private final static String ENABLED = "true"; | |||
public static String getEndPoints() { | |||
return SentinelConfig.getConfig(END_POINTS); | |||
} | |||
@@ -50,25 +48,21 @@ public final class EtcdConfig { | |||
} | |||
public static String getCharset() { | |||
String etcdCharSet = SentinelConfig.getConfig(CHARSET); | |||
if (StringUtil.isNotBlank(etcdCharSet)) { | |||
return etcdCharSet; | |||
String etcdCharset = SentinelConfig.getConfig(CHARSET); | |||
if (StringUtil.isNotBlank(etcdCharset)) { | |||
return etcdCharset; | |||
} | |||
return SentinelConfig.charset(); | |||
} | |||
public static boolean isAuthEnable() { | |||
if (ENABLED.equalsIgnoreCase(SentinelConfig.getConfig(AUTH_ENABLE))) { | |||
return true; | |||
} | |||
return false; | |||
return ENABLED.equalsIgnoreCase(SentinelConfig.getConfig(AUTH_ENABLE)); | |||
} | |||
public static String getAuthority() { | |||
return SentinelConfig.getConfig(AUTHORITY); | |||
} | |||
private EtcdConfig() { | |||
}; | |||
private EtcdConfig() {} | |||
} |
@@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.datasource.etcd; | |||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource; | |||
import com.alibaba.csp.sentinel.datasource.Converter; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import io.etcd.jetcd.ByteSequence; | |||
import io.etcd.jetcd.Client; | |||
import io.etcd.jetcd.KeyValue; | |||
@@ -38,48 +39,46 @@ import java.util.concurrent.CompletableFuture; | |||
*/ | |||
public class EtcdDataSource<T> extends AbstractDataSource<String, T> { | |||
private Client client; | |||
private final Client client; | |||
private Watch.Watcher watcher; | |||
private String key; | |||
private final String key; | |||
private Charset charset = Charset.forName(EtcdConfig.getCharset()); | |||
/** | |||
* Create Etcd Data Source, Retrieve Connect Config Properties from ${@link EtcdConfig} | |||
* Create an etcd data-source. The connection configuration will be retrieved from {@link EtcdConfig}. | |||
* | |||
* @param key Config key | |||
* @param parser Value Parser | |||
* @param key config key | |||
* @param parser data parser | |||
*/ | |||
public EtcdDataSource(String key, Converter<String, T> parser) { | |||
super(parser); | |||
if (!EtcdConfig.isAuthEnable()) { | |||
this.client = Client.builder() | |||
.endpoints(EtcdConfig.getEndPoints().split(",")).build(); | |||
.endpoints(EtcdConfig.getEndPoints().split(",")).build(); | |||
} else { | |||
this.client = Client.builder() | |||
.endpoints(EtcdConfig.getEndPoints().split(",")) | |||
.user(ByteSequence.from(EtcdConfig.getUser(), charset)) | |||
.password(ByteSequence.from(EtcdConfig.getPassword(), charset)) | |||
.authority(EtcdConfig.getAuthority()) | |||
.build(); | |||
.endpoints(EtcdConfig.getEndPoints().split(",")) | |||
.user(ByteSequence.from(EtcdConfig.getUser(), charset)) | |||
.password(ByteSequence.from(EtcdConfig.getPassword(), charset)) | |||
.authority(EtcdConfig.getAuthority()) | |||
.build(); | |||
} | |||
this.key = key; | |||
loadInitialConfig(); | |||
initWatcher(); | |||
} | |||
private void loadInitialConfig() { | |||
try { | |||
T newValue = loadConfig(); | |||
if (newValue == null) { | |||
RecordLog.warn("[EtcdDataSource] WARN: initial application is null, you may have to check your data source"); | |||
RecordLog.warn( | |||
"[EtcdDataSource] Initial configuration is null, you may have to check your data source"); | |||
} | |||
getProperty().updateValue(newValue); | |||
} catch (Exception ex) { | |||
RecordLog.warn("[EtcdDataSource] Error when loading initial application", ex); | |||
RecordLog.warn("[EtcdDataSource] Error when loading initial configuration", ex); | |||
} | |||
} | |||
@@ -92,17 +91,16 @@ public class EtcdDataSource<T> extends AbstractDataSource<String, T> { | |||
T newValue = loadConfig(); | |||
getProperty().updateValue(newValue); | |||
} catch (Exception e) { | |||
RecordLog.warn("[EtcdDataSource] update rule config error: ", e); | |||
RecordLog.warn("[EtcdDataSource] Failed to update config", e); | |||
} | |||
} else if (eventType == WatchEvent.EventType.DELETE) { | |||
RecordLog.info("[EtcdDataSource] Cleaning config for key <{0}>", key); | |||
getProperty().updateValue(null); | |||
RecordLog.info("[EtcdDataSource] clean rule config of {0}", key); | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
public String readSource() throws Exception { | |||
CompletableFuture<GetResponse> responseFuture = client.getKVClient().get(ByteSequence.from(key, charset)); | |||
@@ -116,7 +114,7 @@ public class EtcdDataSource<T> extends AbstractDataSource<String, T> { | |||
try { | |||
watcher.close(); | |||
} catch (Exception ex) { | |||
RecordLog.info("[EtcdDataSource] close watcher error", ex); | |||
RecordLog.info("[EtcdDataSource] Failed to close watcher", ex); | |||
} | |||
} | |||
if (client != null) { | |||