Extensible data source implementations for Sentinel flow control and circuit breaker library.
—
File-based data source implementations providing automatic refresh capabilities for local file systems with configurable buffer sizes, character encodings, and refresh intervals.
The FileRefreshableDataSource class provides file-based readable data source with automatic refresh when the underlying file is modified.
/**
* A ReadableDataSource based on file. This class will automatically
* fetches the backend file every refresh period when file is modified.
* Limitations: Default read buffer size is 1 MB. If file size is greater than
* buffer size, exceeding bytes will be ignored. Default charset is UTF-8.
* @param <T> target data type
*/
public class FileRefreshableDataSource<T> extends AutoRefreshDataSource<String, T> {
// Constants
public static final long DEFAULT_REFRESH_MS = 3000L;
public static final int DEFAULT_BUF_SIZE = 1024 * 1024; // 1 MB
public static final int MAX_SIZE = 1024 * 1024 * 4; // 4 MB
/**
* Create a file based ReadableDataSource with default settings.
* Buffer size: 1MB, charset: UTF-8, refresh interval: 3 seconds.
* @param file the file to read
* @param configParser the config decoder (parser)
* @throws FileNotFoundException if file doesn't exist or is a directory
*/
public FileRefreshableDataSource(File file, Converter<String, T> configParser)
throws FileNotFoundException;
/**
* Create a file based ReadableDataSource with filename.
* @param fileName the file name to read
* @param configParser the config decoder (parser)
* @throws FileNotFoundException if file doesn't exist or is a directory
*/
public FileRefreshableDataSource(String fileName, Converter<String, T> configParser)
throws FileNotFoundException;
/**
* Create a file based ReadableDataSource with custom buffer size.
* @param file the file to read
* @param configParser the config decoder (parser)
* @param bufSize buffer size for reading (must be between 0 and MAX_SIZE)
* @throws FileNotFoundException if file doesn't exist or is a directory
* @throws IllegalArgumentException if bufSize is invalid
*/
public FileRefreshableDataSource(File file, Converter<String, T> configParser, int bufSize)
throws FileNotFoundException;
/**
* Create a file based ReadableDataSource with custom charset.
* @param file the file to read
* @param configParser the config decoder (parser)
* @param charset character encoding for reading the file
* @throws FileNotFoundException if file doesn't exist or is a directory
* @throws IllegalArgumentException if charset is null
*/
public FileRefreshableDataSource(File file, Converter<String, T> configParser, Charset charset)
throws FileNotFoundException;
/**
* Create a file based ReadableDataSource with full customization.
* @param file the file to read
* @param configParser the config decoder (parser)
* @param recommendRefreshMs refresh interval in milliseconds
* @param bufSize buffer size for reading (must be between 0 and MAX_SIZE)
* @param charset character encoding for reading the file
* @throws FileNotFoundException if file doesn't exist or is a directory
* @throws IllegalArgumentException if parameters are invalid
*/
public FileRefreshableDataSource(File file, Converter<String, T> configParser,
long recommendRefreshMs, int bufSize, Charset charset) throws FileNotFoundException;
/**
* Read file content as string.
* @return file content as string with specified charset
* @throws Exception if file reading fails
*/
public String readSource() throws Exception;
/**
* Check if file was modified since last read.
* @return true if file modification time changed
*/
protected boolean isModified();
/**
* Close data source and clean up resources.
* @throws Exception if cleanup fails
*/
public void close() throws Exception;
}Usage Examples:
// Basic file monitoring with JSON rules
Converter<String, List<FlowRule>> jsonConverter = source -> {
if (source == null || source.trim().isEmpty()) {
return new ArrayList<>();
}
return JSON.parseArray(source, FlowRule.class);
};
// Monitor flow rules file
FileRefreshableDataSource<List<FlowRule>> flowRuleDs =
new FileRefreshableDataSource<>(
new File("/sentinel/flow-rules.json"),
jsonConverter
);
// Register for automatic rule updates
flowRuleDs.getProperty().addListener(rules -> {
FlowRuleManager.loadRules(rules);
System.out.println("Flow rules updated: " + rules.size() + " rules");
});
// Custom buffer size and charset for large configuration files
FileRefreshableDataSource<List<DegradeRule>> degradeRuleDs =
new FileRefreshableDataSource<>(
new File("/sentinel/degrade-rules.xml"),
xmlConverter,
2 * 1024 * 1024, // 2MB buffer
StandardCharsets.UTF_16
);
// Fast refresh for development
FileRefreshableDataSource<List<SystemRule>> devSystemRuleDs =
new FileRefreshableDataSource<>(
new File("/dev/system-rules.json"),
systemRuleConverter,
1000, // 1 second refresh
DEFAULT_BUF_SIZE,
StandardCharsets.UTF_8
);The FileWritableDataSource class provides thread-safe file writing capabilities for persisting configuration changes.
/**
* A WritableDataSource based on file.
* @param <T> data type
*/
public class FileWritableDataSource<T> implements WritableDataSource<T> {
/**
* Create a file writable data source with file path.
* @param filePath path to the target file
* @param configEncoder converter from data to string
* @throws IllegalArgumentException if parameters are invalid
*/
public FileWritableDataSource(String filePath, Converter<T, String> configEncoder);
/**
* Create a file writable data source with File object.
* @param file the target file
* @param configEncoder converter from data to string
* @throws IllegalArgumentException if file is null, directory, or configEncoder is null
*/
public FileWritableDataSource(File file, Converter<T, String> configEncoder);
/**
* Create a file writable data source with custom charset.
* @param file the target file
* @param configEncoder converter from data to string
* @param charset character encoding for writing
* @throws IllegalArgumentException if any parameter is null or file is directory
*/
public FileWritableDataSource(File file, Converter<T, String> configEncoder, Charset charset);
/**
* Write value to file in a thread-safe manner.
* @param value value to write
* @throws Exception if file writing fails
*/
public void write(T value) throws Exception;
/**
* Close data source (no-op for file implementation).
* @throws Exception if cleanup fails
*/
public void close() throws Exception;
}Usage Examples:
// JSON encoder for flow rules
Converter<List<FlowRule>, String> flowRuleEncoder = rules -> {
return JSON.toJSONString(rules, true); // Pretty print
};
// Writable data source for persisting rule changes
FileWritableDataSource<List<FlowRule>> writableDs =
new FileWritableDataSource<>(
"/sentinel/flow-rules.json",
flowRuleEncoder
);
// Write updated rules
List<FlowRule> newRules = Arrays.asList(
new FlowRule()
.setResource("GET:/api/users")
.setCount(100)
.setGrade(RuleConstant.FLOW_GRADE_QPS),
new FlowRule()
.setResource("POST:/api/orders")
.setCount(50)
.setGrade(RuleConstant.FLOW_GRADE_QPS)
);
writableDs.write(newRules);
// XML encoder with custom charset
Converter<List<DegradeRule>, String> xmlEncoder = rules -> {
StringBuilder xml = new StringBuilder();
xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
xml.append("<rules>\n");
for (DegradeRule rule : rules) {
xml.append(" <rule resource=\"").append(rule.getResource()).append("\"")
.append(" count=\"").append(rule.getCount()).append("\"")
.append(" timeWindow=\"").append(rule.getTimeWindow()).append("\"/>\n");
}
xml.append("</rules>");
return xml.toString();
};
FileWritableDataSource<List<DegradeRule>> xmlWriter =
new FileWritableDataSource<>(
new File("/config/degrade-rules.xml"),
xmlEncoder,
StandardCharsets.UTF_8
);Combine readable and writable data sources for complete configuration management:
// Setup read and write data sources for the same file
File configFile = new File("/sentinel/flow-rules.json");
Converter<String, List<FlowRule>> reader = source ->
JSON.parseArray(source, FlowRule.class);
Converter<List<FlowRule>, String> writer = rules ->
JSON.toJSONString(rules, true);
// Readable data source with auto-refresh
FileRefreshableDataSource<List<FlowRule>> readableDs =
new FileRefreshableDataSource<>(configFile, reader);
// Writable data source for updates
FileWritableDataSource<List<FlowRule>> writableDs =
new FileWritableDataSource<>(configFile, writer);
// Dynamic rule management
readableDs.getProperty().addListener(FlowRuleManager::loadRules);
// API endpoint for rule updates
@PostMapping("/flow-rules")
public ResponseEntity<String> updateFlowRules(@RequestBody List<FlowRule> rules) {
try {
writableDs.write(rules);
return ResponseEntity.ok("Rules updated successfully");
} catch (Exception e) {
return ResponseEntity.status(500).body("Failed to update rules: " + e.getMessage());
}
}Support multiple configuration formats through different converters:
// YAML converter
Converter<String, List<FlowRule>> yamlConverter = source -> {
Yaml yaml = new Yaml();
Map<String, Object> data = yaml.load(source);
List<Map<String, Object>> rulesData = (List<Map<String, Object>>) data.get("flowRules");
return rulesData.stream()
.map(ruleData -> new FlowRule()
.setResource((String) ruleData.get("resource"))
.setCount(((Number) ruleData.get("count")).doubleValue()))
.collect(Collectors.toList());
};
// Properties converter
Converter<String, List<FlowRule>> propsConverter = source -> {
Properties props = new Properties();
props.load(new StringReader(source));
List<FlowRule> rules = new ArrayList<>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith("flow.rule.")) {
String resource = key.substring("flow.rule.".length());
double count = Double.parseDouble(props.getProperty(key));
rules.add(new FlowRule().setResource(resource).setCount(count));
}
}
return rules;
};ReentrantLock for thread-safe writesInstall with Tessl CLI
npx tessl i tessl/maven-com-alibaba-csp--sentinel-datasource-extension