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,68 @@
/*
* 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.etcd;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Etcd connection configuration.
*
* @author lianglin
* @since 1.7.0
*/
public final class EtcdConfig {
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";
public final static String AUTH_ENABLE = "csp.sentinel.etcd.auth.enable";
public final static String AUTHORITY = "csp.sentinel.etcd.authority";
private final static String ENABLED = "true";
public static String getEndPoints() {
return SentinelConfig.getConfig(END_POINTS);
}
public static String getUser() {
return SentinelConfig.getConfig(USER);
}
public static String getPassword() {
return SentinelConfig.getConfig(PASSWORD);
}
public static String getCharset() {
String etcdCharset = SentinelConfig.getConfig(CHARSET);
if (StringUtil.isNotBlank(etcdCharset)) {
return etcdCharset;
}
return SentinelConfig.charset();
}
public static boolean isAuthEnable() {
return ENABLED.equalsIgnoreCase(SentinelConfig.getConfig(AUTH_ENABLE));
}
public static String getAuthority() {
return SentinelConfig.getConfig(AUTHORITY);
}
private EtcdConfig() {}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.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;
import io.etcd.jetcd.Watch;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.watch.WatchEvent;
import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* A read-only {@code DataSource} with Etcd backend. When the data in Etcd backend has been modified,
* Etcd will automatically push the new value so that the dynamic configuration can be real-time.
*
* @author lianglin
* @since 1.7.0
*/
public class EtcdDataSource<T> extends AbstractDataSource<String, T> {
private final Client client;
private Watch.Watcher watcher;
private final String key;
private Charset charset = Charset.forName(EtcdConfig.getCharset());
/**
* Create an etcd data-source. The connection configuration will be retrieved from {@link EtcdConfig}.
*
* @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();
} 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();
}
this.key = key;
loadInitialConfig();
initWatcher();
}
private void loadInitialConfig() {
try {
T newValue = loadConfig();
if (newValue == null) {
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 configuration", ex);
}
}
private void initWatcher() {
watcher = client.getWatchClient().watch(ByteSequence.from(key, charset), (watchResponse) -> {
for (WatchEvent watchEvent : watchResponse.getEvents()) {
WatchEvent.EventType eventType = watchEvent.getEventType();
if (eventType == WatchEvent.EventType.PUT) {
try {
T newValue = loadConfig();
getProperty().updateValue(newValue);
} catch (Exception e) {
RecordLog.warn("[EtcdDataSource] Failed to update config", e);
}
} else if (eventType == WatchEvent.EventType.DELETE) {
RecordLog.info("[EtcdDataSource] Cleaning config for key <{}>", key);
getProperty().updateValue(null);
}
}
});
}
@Override
public String readSource() throws Exception {
CompletableFuture<GetResponse> responseFuture = client.getKVClient().get(ByteSequence.from(key, charset));
List<KeyValue> kvs = responseFuture.get().getKvs();
return kvs.size() == 0 ? null : kvs.get(0).getValue().toString(charset);
}
@Override
public void close() {
if (watcher != null) {
try {
watcher.close();
} catch (Exception ex) {
RecordLog.info("[EtcdDataSource] Failed to close watcher", ex);
}
}
if (client != null) {
client.close();
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* 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.etcd;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.KV;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author lianglin
* @since 1.7.0
*/
@Ignore(value = "Before run this test, you need to set up your etcd server.")
public class EtcdDataSourceTest {
private final String endPoints = "http://127.0.0.1:2379";
@Before
public void setUp() {
SentinelConfig.setConfig(EtcdConfig.END_POINTS, endPoints);
FlowRuleManager.loadRules(new ArrayList<>());
}
@After
public void tearDown() {
SentinelConfig.setConfig(EtcdConfig.END_POINTS, "");
FlowRuleManager.loadRules(new ArrayList<>());
}
@Test
public void testReadSource() throws Exception {
EtcdDataSource dataSource = new EtcdDataSource("foo", value -> value);
KV kvClient = Client.builder()
.endpoints(endPoints)
.build().getKVClient();
kvClient.put(ByteSequence.from("foo".getBytes()), ByteSequence.from("test".getBytes()));
Assert.assertNotNull(dataSource.readSource().equals("test"));
kvClient.put(ByteSequence.from("foo".getBytes()), ByteSequence.from("test2".getBytes()));
Assert.assertNotNull(dataSource.getProperty().equals("test2"));
}
@Test
public void testDynamicUpdate() throws InterruptedException {
String demo_key = "etcd_demo_key";
ReadableDataSource<String, List<FlowRule>> flowRuleEtcdDataSource = new EtcdDataSource<>(demo_key, (value) -> JSON.parseArray(value, FlowRule.class));
FlowRuleManager.register2Property(flowRuleEtcdDataSource.getProperty());
KV kvClient = Client.builder()
.endpoints(endPoints)
.build().getKVClient();
final String rule1 = "[\n"
+ " {\n"
+ " \"resource\": \"TestResource\",\n"
+ " \"controlBehavior\": 0,\n"
+ " \"count\": 5.0,\n"
+ " \"grade\": 1,\n"
+ " \"limitApp\": \"default\",\n"
+ " \"strategy\": 0\n"
+ " }\n"
+ "]";
kvClient.put(ByteSequence.from(demo_key.getBytes()), ByteSequence.from(rule1.getBytes()));
Thread.sleep(1000);
FlowRule flowRule = FlowRuleManager.getRules().get(0);
Assert.assertTrue(flowRule.getResource().equals("TestResource"));
Assert.assertTrue(flowRule.getCount() == 5.0);
Assert.assertTrue(flowRule.getGrade() == 1);
final String rule2 = "[\n"
+ " {\n"
+ " \"resource\": \"TestResource\",\n"
+ " \"controlBehavior\": 0,\n"
+ " \"count\": 6.0,\n"
+ " \"grade\": 3,\n"
+ " \"limitApp\": \"default\",\n"
+ " \"strategy\": 0\n"
+ " }\n"
+ "]";
kvClient.put(ByteSequence.from(demo_key.getBytes()), ByteSequence.from(rule2.getBytes()));
Thread.sleep(1000);
flowRule = FlowRuleManager.getRules().get(0);
Assert.assertTrue(flowRule.getResource().equals("TestResource"));
Assert.assertTrue(flowRule.getCount() == 6.0);
Assert.assertTrue(flowRule.getGrade() == 3);
}
}