CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-alibaba-csp--sentinel-datasource-extension

Extensible data source implementations for Sentinel flow control and circuit breaker library.

Pending
Overview
Eval results
Files

file-datasources.mddocs/

File Data Sources

File-based data source implementations providing automatic refresh capabilities for local file systems with configurable buffer sizes, character encodings, and refresh intervals.

Capabilities

FileRefreshableDataSource Class

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
    );

FileWritableDataSource Class

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
    );

Integration Patterns

Read-Write Configuration Management

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());
    }
}

Configuration File Formats

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;
};

Error Handling and Limitations

File Size Limitations

  • Default buffer size: 1 MB
  • Maximum buffer size: 4 MB
  • Behavior: Files larger than buffer size will be truncated
  • Recommendation: Use appropriate buffer size for your configuration files

Character Encoding

  • Default charset: UTF-8
  • Custom charset: Specify in constructor
  • Encoding consistency: Ensure read and write operations use same charset

Thread Safety

  • FileRefreshableDataSource: Thread-safe for concurrent reads
  • FileWritableDataSource: Uses ReentrantLock for thread-safe writes
  • File system: Handles concurrent access through proper locking mechanisms

Install with Tessl CLI

npx tessl i tessl/maven-com-alibaba-csp--sentinel-datasource-extension

docs

base-classes.md

core-interfaces.md

file-datasources.md

index.md

specialized-datasources.md

tile.json