From 007cd9d29186ac31008b6d2a695bc28e4c4b2678 Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Tue, 4 Sep 2018 11:00:20 +0800 Subject: [PATCH] Some refactor of data source - Add `close` method in WritableDataSource (to extend AutoCloseable in JDK 1.7 later) - Separate the writable file data source from original class - Add a sample to show how to register data sources via Sentinel init mechanism - Separate a writable data source registry from original handler to make it clear Signed-off-by: Eric Zhao --- .../sentinel-demo-dynamic-file-rule/pom.xml | 4 ++ .../demo/file/rule/FileDataSourceDemo.java | 10 +-- .../demo/file/rule/FileDataSourceInit.java | 67 +++++++++++++++++++ .../datasource/FileRefreshableDataSource.java | 33 +++------ .../datasource/FileWritableDataSource.java | 56 ++++++++++++++++ .../datasource/WritableDataSource.java | 7 ++ .../handler/ModifyRulesCommandHandler.java | 31 ++------- .../util/WritableDataSourceRegistry.java | 56 ++++++++++++++++ 8 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceInit.java create mode 100644 sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileWritableDataSource.java create mode 100644 sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/transport/util/WritableDataSourceRegistry.java diff --git a/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml b/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml index 24491ba1..5dc0b2ad 100755 --- a/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml +++ b/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml @@ -19,6 +19,10 @@ com.alibaba.csp sentinel-datasource-extension + + com.alibaba.csp + sentinel-transport-simple-http + com.alibaba diff --git a/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceDemo.java b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceDemo.java index 0dda2a2a..bd5591af 100644 --- a/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceDemo.java +++ b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceDemo.java @@ -81,19 +81,19 @@ public class FileDataSourceDemo { // Data source for FlowRule FileRefreshableDataSource> flowRuleDataSource = new FileRefreshableDataSource<>( - flowRulePath, flowRuleListParser, this::encodeJson); + flowRulePath, flowRuleListParser); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); // Data source for DegradeRule FileRefreshableDataSource> degradeRuleDataSource = new FileRefreshableDataSource<>( - degradeRulePath, degradeRuleListParser, this::encodeJson); + degradeRulePath, degradeRuleListParser); DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty()); // Data source for SystemRule FileRefreshableDataSource> systemRuleDataSource = new FileRefreshableDataSource<>( - systemRulePath, systemRuleListParser, this::encodeJson); + systemRulePath, systemRuleListParser); SystemRuleManager.register2Property(systemRuleDataSource.getProperty()); } @@ -103,8 +103,4 @@ public class FileDataSourceDemo { new TypeReference>() {}); private Converter> systemRuleListParser = source -> JSON.parseObject(source, new TypeReference>() {}); - - private String encodeJson(T t) { - return JSON.toJSONString(t); - } } diff --git a/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceInit.java b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceInit.java new file mode 100644 index 00000000..76b20a80 --- /dev/null +++ b/sentinel-demo/sentinel-demo-dynamic-file-rule/src/main/java/com/alibaba/csp/sentinel/demo/file/rule/FileDataSourceInit.java @@ -0,0 +1,67 @@ +/* + * 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.demo.file.rule; + +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource; +import com.alibaba.csp.sentinel.datasource.FileWritableDataSource; +import com.alibaba.csp.sentinel.datasource.ReadableDataSource; +import com.alibaba.csp.sentinel.datasource.WritableDataSource; +import com.alibaba.csp.sentinel.init.InitFunc; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; + +/** + *

+ * A sample showing how to register readable and writable data source via Sentinel init SPI mechanism. + *

+ *

+ * To activate this, you can add the class name to `com.alibaba.csp.sentinel.init.InitFunc` file + * in `META-INF/services/` directory of the resource directory. Then the data source will be automatically + * registered during the initialization of Sentinel. + *

+ * + * @author Eric Zhao + */ +public class FileDataSourceInit implements InitFunc { + + @Override + public void init() throws Exception { + // A fake path. + String flowRuleDir = System.getProperty("user.home") + "/sentinel/rules"; + String flowRuleFile = "flowRule.json"; + String flowRulePath = flowRuleDir + "/" + flowRuleFile; + + ReadableDataSource> ds = new FileRefreshableDataSource<>( + flowRulePath, source -> JSON.parseObject(source, new TypeReference>() {}) + ); + // Register to flow rule manager. + FlowRuleManager.register2Property(ds.getProperty()); + + WritableDataSource> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson); + // Register to writable data source registry so that rules can be updated to file + // when there are rules pushed from the Sentinel Dashboard. + WritableDataSourceRegistry.registerFlowDataSource(wds); + } + + private String encodeJson(T t) { + return JSON.toJSONString(t); + } +} diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileRefreshableDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileRefreshableDataSource.java index 7183dd75..ac141f8e 100755 --- a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileRefreshableDataSource.java +++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/FileRefreshableDataSource.java @@ -25,7 +25,7 @@ import com.alibaba.csp.sentinel.log.RecordLog; /** *

- * A {@link WritableDataSource} based on file. This class will automatically fetches the backend file every refresh period. + * A {@link ReadableDataSource} based on file. This class will automatically fetches the backend file every refresh period. *

*

* Limitations: Default read buffer size is 1 MB. If file size is greater than buffer size, exceeding bytes will @@ -35,7 +35,7 @@ import com.alibaba.csp.sentinel.log.RecordLog; * @author Carpenter Lee * @author Eric Zhao */ -public class FileRefreshableDataSource extends AutoRefreshDataSource implements WritableDataSource { +public class FileRefreshableDataSource extends AutoRefreshDataSource { private static final int MAX_SIZE = 1024 * 1024 * 4; private static final long DEFAULT_REFRESH_MS = 3000; @@ -46,8 +46,6 @@ public class FileRefreshableDataSource extends AutoRefreshDataSource configEncoder; - /** * Create a file based {@link ReadableDataSource} whose read buffer size is 1MB, charset is UTF8, * and read interval is 3 seconds. @@ -55,26 +53,26 @@ public class FileRefreshableDataSource extends AutoRefreshDataSource configParser, Converter configEncoder) throws FileNotFoundException { - this(file, configParser, configEncoder, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET); + public FileRefreshableDataSource(File file, Converter configParser) throws FileNotFoundException { + this(file, configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET); } - public FileRefreshableDataSource(String fileName, Converter configParser, Converter configEncoder) + public FileRefreshableDataSource(String fileName, Converter configParser) throws FileNotFoundException { - this(new File(fileName), configParser, configEncoder, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET); + this(new File(fileName), configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET); } - public FileRefreshableDataSource(File file, Converter configParser, Converter configEncoder, int bufSize) + public FileRefreshableDataSource(File file, Converter configParser, int bufSize) throws FileNotFoundException { - this(file, configParser, configEncoder, DEFAULT_REFRESH_MS, bufSize, DEFAULT_CHAR_SET); + this(file, configParser, DEFAULT_REFRESH_MS, bufSize, DEFAULT_CHAR_SET); } - public FileRefreshableDataSource(File file, Converter configParser, Converter configEncoder, Charset charset) + public FileRefreshableDataSource(File file, Converter configParser, Charset charset) throws FileNotFoundException { - this(file, configParser, configEncoder, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, charset); + this(file, configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, charset); } - public FileRefreshableDataSource(File file, Converter configParser, Converter configEncoder, long recommendRefreshMs, + public FileRefreshableDataSource(File file, Converter configParser, long recommendRefreshMs, int bufSize, Charset charset) throws FileNotFoundException { super(configParser, recommendRefreshMs); if (bufSize <= 0 || bufSize > MAX_SIZE) { @@ -86,13 +84,9 @@ public class FileRefreshableDataSource extends AutoRefreshDataSource extends AutoRefreshDataSource data type + * @author Eric Zhao + * @since 0.2.0 + */ +public class FileWritableDataSource implements WritableDataSource { + + private final Converter configEncoder; + private File file; + + public FileWritableDataSource(String filePath, Converter configEncoder) { + this(new File(filePath), configEncoder); + } + + public FileWritableDataSource(File file, Converter configEncoder) { + if (file == null || file.isDirectory()) { + throw new IllegalArgumentException("Bad file"); + } + if (configEncoder == null) { + throw new IllegalArgumentException("Config encoder cannot be null"); + } + this.configEncoder = configEncoder; + this.file = file; + } + + @Override + public void write(T value) throws Exception { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public void close() throws Exception { + // Nothing + } +} diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/WritableDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/WritableDataSource.java index f63dd21d..76a6a031 100644 --- a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/WritableDataSource.java +++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/WritableDataSource.java @@ -30,4 +30,11 @@ public interface WritableDataSource { * @throws Exception IO or other error occurs */ void write(T value) throws Exception; + + /** + * Close the data source. + * + * @throws Exception IO or other error occurs + */ + void close() throws Exception; } diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyRulesCommandHandler.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyRulesCommandHandler.java index 28556ba4..126a16dc 100755 --- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyRulesCommandHandler.java +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyRulesCommandHandler.java @@ -35,6 +35,8 @@ import com.alibaba.csp.sentinel.slots.system.SystemRuleManager; import com.alibaba.csp.sentinel.slots.system.SystemRule; import com.alibaba.fastjson.JSONArray; +import static com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry.*; + /** * @author jialiang.linjl * @author Eric Zhao @@ -42,27 +44,6 @@ import com.alibaba.fastjson.JSONArray; @CommandMapping(name = "setRules") public class ModifyRulesCommandHandler implements CommandHandler { - private static WritableDataSource> flowDataSource = null; - private static WritableDataSource> authorityDataSource = null; - private static WritableDataSource> degradeDataSource = null; - private static WritableDataSource> systemSource = null; - - public static synchronized void registerFlowDataSource(WritableDataSource> datasource) { - flowDataSource = datasource; - } - - public static synchronized void registerAuthorityDataSource(WritableDataSource> dataSource) { - authorityDataSource = dataSource; - } - - public static synchronized void registerDegradeDataSource(WritableDataSource> dataSource) { - degradeDataSource = dataSource; - } - - public static synchronized void registerSystemDataSource(WritableDataSource> dataSource) { - systemSource = dataSource; - } - @Override public CommandResponse handle(CommandRequest request) { String type = request.getParam("type"); @@ -84,28 +65,28 @@ public class ModifyRulesCommandHandler implements CommandHandler { if (FLOW_RULE_TYPE.equalsIgnoreCase(type)) { List flowRules = JSONArray.parseArray(data, FlowRule.class); FlowRuleManager.loadRules(flowRules); - if (!writeToDataSource(flowDataSource, flowRules)) { + if (!writeToDataSource(getFlowDataSource(), flowRules)) { result = WRITE_DS_FAILURE_MSG; } return CommandResponse.ofSuccess(result); } else if (AUTHORITY_RULE_TYPE.equalsIgnoreCase(type)) { List rules = JSONArray.parseArray(data, AuthorityRule.class); AuthorityRuleManager.loadRules(rules); - if (!writeToDataSource(authorityDataSource, rules)) { + if (!writeToDataSource(getAuthorityDataSource(), rules)) { result = WRITE_DS_FAILURE_MSG; } return CommandResponse.ofSuccess(result); } else if (DEGRADE_RULE_TYPE.equalsIgnoreCase(type)) { List rules = JSONArray.parseArray(data, DegradeRule.class); DegradeRuleManager.loadRules(rules); - if (!writeToDataSource(degradeDataSource, rules)) { + if (!writeToDataSource(getDegradeDataSource(), rules)) { result = WRITE_DS_FAILURE_MSG; } return CommandResponse.ofSuccess(result); } else if (SYSTEM_RULE_TYPE.equalsIgnoreCase(type)) { List rules = JSONArray.parseArray(data, SystemRule.class); SystemRuleManager.loadRules(rules); - if (!writeToDataSource(systemSource, rules)) { + if (!writeToDataSource(getSystemSource(), rules)) { result = WRITE_DS_FAILURE_MSG; } return CommandResponse.ofSuccess(result); diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/transport/util/WritableDataSourceRegistry.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/transport/util/WritableDataSourceRegistry.java new file mode 100644 index 00000000..bfab6268 --- /dev/null +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/transport/util/WritableDataSourceRegistry.java @@ -0,0 +1,56 @@ +package com.alibaba.csp.sentinel.transport.util; + +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.WritableDataSource; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.system.SystemRule; + +/** + * Writable data source registry for modifying rules via HTTP API. + * + * @author Eric Zhao + */ +public final class WritableDataSourceRegistry { + + private static WritableDataSource> flowDataSource = null; + private static WritableDataSource> authorityDataSource = null; + private static WritableDataSource> degradeDataSource = null; + private static WritableDataSource> systemSource = null; + + public static synchronized void registerFlowDataSource(WritableDataSource> datasource) { + flowDataSource = datasource; + } + + public static synchronized void registerAuthorityDataSource(WritableDataSource> dataSource) { + authorityDataSource = dataSource; + } + + public static synchronized void registerDegradeDataSource(WritableDataSource> dataSource) { + degradeDataSource = dataSource; + } + + public static synchronized void registerSystemDataSource(WritableDataSource> dataSource) { + systemSource = dataSource; + } + + public static WritableDataSource> getFlowDataSource() { + return flowDataSource; + } + + public static WritableDataSource> getAuthorityDataSource() { + return authorityDataSource; + } + + public static WritableDataSource> getDegradeDataSource() { + return degradeDataSource; + } + + public static WritableDataSource> getSystemSource() { + return systemSource; + } + + private WritableDataSourceRegistry() {} +}