>() {
+ });
+ }
+ });
+
+ FlowRuleManager.register2Property(eurekaDataSource.getProperty());
+ return eurekaDataSource;
+}
+
+```
+
+To refresh the rule dynamically,you need to call [Eureka-REST-operations](https://github.com/Netflix/eureka/wiki/Eureka-REST-operations)
+to update instance metadata:
+
+```
+PUT /eureka/apps/{appID}/{instanceID}/metadata?{ruleKey}={json of the rules}
+```
+
+Note: don't forget to encode your json string in the url.
\ No newline at end of file
diff --git a/sentinel-extension/sentinel-datasource-eureka/pom.xml b/sentinel-extension/sentinel-datasource-eureka/pom.xml
new file mode 100644
index 00000000..666f10e1
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-eureka/pom.xml
@@ -0,0 +1,66 @@
+
+
+
+ sentinel-extension
+ com.alibaba.csp
+ 1.8.0-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-datasource-eureka
+
+
+ 2.1.2.RELEASE
+
+
+
+
+
+ com.alibaba.csp
+ sentinel-datasource-extension
+
+
+
+ com.alibaba
+ fastjson
+
+
+
+ junit
+ junit
+ test
+
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.cloud.version}
+ test
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-server
+ ${spring.cloud.version}
+ test
+
+
+ com.google.code.gson
+ gson
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-extension/sentinel-datasource-eureka/src/main/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSource.java b/sentinel-extension/sentinel-datasource-eureka/src/main/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSource.java
new file mode 100644
index 00000000..8029e8e9
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-eureka/src/main/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSource.java
@@ -0,0 +1,213 @@
+/*
+ * 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.eureka;
+
+import com.alibaba.csp.sentinel.datasource.AutoRefreshDataSource;
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.fastjson.JSON;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * A {@link ReadableDataSource} based on Eureka. This class will automatically
+ * fetches the metadata of the instance every period.
+ *
+ *
+ * Limitations: Default refresh interval is 10s. Because there is synchronization between eureka servers,
+ * it may take longer to take effect.
+ *
+ *
+ * @author: liyang
+ * @create: 2020-05-23 12:01
+ */
+public class EurekaDataSource extends AutoRefreshDataSource {
+
+ private static final long DEFAULT_REFRESH_MS = 10000;
+
+ /**
+ * connect timeout: 3s
+ */
+ private static final int DEFAULT_CONNECT_TIMEOUT_MS = 3000;
+
+ /**
+ * read timeout: 30s
+ */
+ private static final int DEFAULT_READ_TIMEOUT_MS = 30000;
+
+
+ private int connectTimeoutMills;
+
+
+ private int readTimeoutMills;
+
+ /**
+ * eureka instance appid
+ */
+ private String appId;
+ /**
+ * eureka instance id
+ */
+ private String instanceId;
+
+ /**
+ * collect of eureka server urls
+ */
+ private List serviceUrls;
+
+ /**
+ * metadata key of the rule source
+ */
+ private String ruleKey;
+
+
+ public EurekaDataSource(String appId, String instanceId, List serviceUrls, String ruleKey,
+ Converter configParser) {
+ this(appId, instanceId, serviceUrls, ruleKey, configParser, DEFAULT_REFRESH_MS, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
+ }
+
+
+ public EurekaDataSource(String appId, String instanceId, List serviceUrls, String ruleKey,
+ Converter configParser, long refreshMs, int connectTimeoutMills,
+ int readTimeoutMills) {
+ super(configParser, refreshMs);
+ AssertUtil.notNull(appId, "appId can't be null");
+ AssertUtil.notNull(instanceId, "instanceId can't be null");
+ AssertUtil.assertNotEmpty(serviceUrls, "serviceUrls can't be empty");
+ AssertUtil.notNull(ruleKey, "ruleKey can't be null");
+ AssertUtil.assertState(connectTimeoutMills > 0, "connectTimeoutMills must be greater than 0");
+ AssertUtil.assertState(readTimeoutMills > 0, "readTimeoutMills must be greater than 0");
+
+ this.appId = appId;
+ this.instanceId = instanceId;
+ this.serviceUrls = ensureEndWithSlash(serviceUrls);
+ AssertUtil.assertNotEmpty(this.serviceUrls, "No available service url");
+ this.ruleKey = ruleKey;
+ this.connectTimeoutMills = connectTimeoutMills;
+ this.readTimeoutMills = readTimeoutMills;
+ }
+
+
+ private List ensureEndWithSlash(List serviceUrls) {
+ List newServiceUrls = new ArrayList<>();
+ for (String serviceUrl : serviceUrls) {
+ if (StringUtil.isBlank(serviceUrl)) {
+ continue;
+ }
+ if (!serviceUrl.endsWith("/")) {
+ serviceUrl = serviceUrl + "/";
+ }
+ newServiceUrls.add(serviceUrl);
+ }
+ return newServiceUrls;
+ }
+
+ @Override
+ public String readSource() throws Exception {
+ return fetchStringSourceFromEurekaMetadata(this.appId, this.instanceId, this.serviceUrls, ruleKey);
+ }
+
+
+ private String fetchStringSourceFromEurekaMetadata(String appId, String instanceId, List serviceUrls,
+ String ruleKey) throws Exception {
+ List shuffleUrls = new ArrayList<>(serviceUrls.size());
+ shuffleUrls.addAll(serviceUrls);
+ Collections.shuffle(shuffleUrls);
+ for (int i = 0; i < shuffleUrls.size(); i++) {
+ String serviceUrl = shuffleUrls.get(i) + String.format("apps/%s/%s", appId, instanceId);
+ HttpURLConnection conn = null;
+ try {
+ conn = (HttpURLConnection) new URL(serviceUrl).openConnection();
+ conn.addRequestProperty("Accept", "application/json;charset=utf-8");
+
+ conn.setConnectTimeout(connectTimeoutMills);
+ conn.setReadTimeout(readTimeoutMills);
+ conn.setRequestMethod("GET");
+ conn.setDoOutput(true);
+ conn.connect();
+ RecordLog.debug("[EurekaDataSource] Request from eureka server: " + serviceUrl);
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ String s = toString(conn.getInputStream());
+ String ruleString = JSON.parseObject(s)
+ .getJSONObject("instance")
+ .getJSONObject("metadata")
+ .getString(ruleKey);
+ return ruleString;
+ }
+ RecordLog.warn("[EurekaDataSource] Warn: retrying on another server if available " +
+ "due to response code: {}, response message: {}", conn.getResponseCode(), toString(conn.getErrorStream()));
+ } catch (Exception e) {
+ try {
+ if (conn != null) {
+ RecordLog.warn("[EurekaDataSource] Warn: failed to request " + conn.getURL() + " from "
+ + InetAddress.getByName(conn.getURL().getHost()).getHostAddress(), e);
+ }
+ } catch (Exception e1) {
+ RecordLog.warn("[EurekaDataSource] Warn: failed to request ", e1);
+ //ignore
+ }
+ RecordLog.warn("[EurekaDataSource] Warn: failed to request,retrying on another server if available");
+
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+ throw new EurekaMetadataFetchException("Can't get any data");
+ }
+
+
+ public static class EurekaMetadataFetchException extends Exception {
+
+ public EurekaMetadataFetchException(String message) {
+ super(message);
+ }
+ }
+
+
+ private String toString(InputStream input) throws IOException {
+ if (input == null) {
+ return null;
+ }
+ InputStreamReader inputStreamReader = new InputStreamReader(input, "utf-8");
+ CharArrayWriter sw = new CharArrayWriter();
+ copy(inputStreamReader, sw);
+ return sw.toString();
+ }
+
+ private long copy(Reader input, Writer output) throws IOException {
+ char[] buffer = new char[1 << 12];
+ long count = 0;
+ for (int n = 0; (n = input.read(buffer)) >= 0; ) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+
+}
diff --git a/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSourceTest.java b/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSourceTest.java
new file mode 100644
index 00000000..05fef90e
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/EurekaDataSourceTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.eureka;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+
+/**
+ * @author liyang
+ */
+@RunWith(SpringRunner.class)
+@EnableEurekaServer
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+public class EurekaDataSourceTest {
+
+ private static final String SENTINEL_KEY = "sentinel-rules";
+
+ @Value("${server.port}")
+ private int port;
+
+ @Value("${eureka.instance.appname}")
+ private String appname;
+
+ @Value("${eureka.instance.instance-id}")
+ private String instanceId;
+
+
+ @Test
+ public void testEurekaDataSource() throws Exception {
+ String url = "http://localhost:" + port + "/eureka";
+
+ EurekaDataSource> eurekaDataSource = new EurekaDataSource(appname, instanceId, Arrays.asList(url)
+ , SENTINEL_KEY, new Converter>() {
+ @Override
+ public List convert(String source) {
+ return JSON.parseObject(source, new TypeReference>() {
+ });
+ }
+ });
+ FlowRuleManager.register2Property(eurekaDataSource.getProperty());
+
+ await().timeout(15, TimeUnit.SECONDS)
+ .until(new Callable() {
+ @Override
+ public Boolean call() throws Exception {
+ return FlowRuleManager.getRules().size() > 0;
+ }
+ });
+ Assert.assertTrue(FlowRuleManager.getRules().size() > 0);
+ }
+
+
+}
diff --git a/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/SimpleSpringApplication.java b/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/SimpleSpringApplication.java
new file mode 100644
index 00000000..872c7ca3
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-eureka/src/test/java/com/alibaba/csp/sentinel/datasource/eureka/SimpleSpringApplication.java
@@ -0,0 +1,31 @@
+/*
+ * 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.eureka;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author liyang
+ */
+@SpringBootApplication
+public class SimpleSpringApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(SimpleSpringApplication.class);
+ }
+
+}
diff --git a/sentinel-extension/sentinel-datasource-eureka/src/test/resources/application.yml b/sentinel-extension/sentinel-datasource-eureka/src/test/resources/application.yml
new file mode 100644
index 00000000..3aae0dda
--- /dev/null
+++ b/sentinel-extension/sentinel-datasource-eureka/src/test/resources/application.yml
@@ -0,0 +1,14 @@
+server:
+ port: 8761
+eureka:
+ instance:
+ instance-id: instance-0
+ appname: testapp
+ metadata-map:
+ sentinel-rules: "[{'clusterMode':false,'controlBehavior':0,'count':20.0,'grade':1,'limitApp':'default','maxQueueingTimeMs':500,'resource':'resource-demo-name','strategy':0,'warmUpPeriodSec':10}]"
+
+ client:
+ register-with-eureka: true
+ fetch-registry: false
+ service-url:
+ defaultZone: http://localhost:8761/eureka/