Hou Yi reproduced please note the original source, thank you!

series

Sentinel principle – Full resolution

Sentinel principle – Call chain

Sentinel Principle – Sliding Windows

Sentinel Principle – Entity classes

Sentinel Combat – Current Limiting Chapter

Sentinel Combat – Console chapter

Five ways to persist rules

Rules of the lost

Neither hard-coding the rules nor operating on the page after the Sentinel Dashboard is plugged in can avoid the problem that the rules are lost when the service is restarted because by default they are stored in memory.

Dashboard obtains the rules in each Sentinel client through the Transport module. The obtained rules are stored in Dashboard’s memory through the RuleRepository interface. If a rule is changed in the Dashboard page, The interface provided by the Transport module is also called to update the rules to the client.

Imagine a situation where the client connects to the Dashboard and we configure the rules for the client on the Dashboard and push them to the client. At this time, due to some factors, the client was abnormal and the service was unavailable. When the client recovered and connected to Dashboard again, all the rules were lost and we needed to reconfigure the rules, which was definitely not what we wanted.

As shown in the figure above, when the Sentinel client dies, the rules stored in the various RuleManagers go up in flames, so this should never be done in production.

Rule persistence principle

What can we do to solve this problem? It is very simple, that is, to persist a copy of the rules originally stored in RuleManager memory. This way, the next time the client restarts, it can load data from the persistent copy into memory, so that the rules are not lost, as shown in the following figure:

Sentinel provides two interfaces for persisting rules: ReadableDataSource and WritableDataSource.

The WritableDataSource is not what we care about, or the WritableDataSource is not that important, because most persistent data sources already provide specific methods to persist data, so we just need to fetch data from the persistent data source. Just convert it to the format we want.

The ReadableDataSource interface is defined as follows:

public interface ReadableDataSource<S.T> {
	// Read raw data from the data source
    S readSource(a) throws Exception;
	// Convert the raw data into the format we want
    T loadConfig(a) throws Exception;
    // Get the SentinelProperty object for the data source
    SentinelProperty<T> getProperty(a);
}
Copy the code

AbstractDataSource class AbstractDataSource implements two methods. The data source implementation class only needs to implement a readSource method. The code is as follows:

public abstract class AbstractDataSource<S.T> 
		implements ReadableDataSource<S.T> {
	// Converter interface is responsible for converting data
    protected final Converter<S, T> parser;
    // The SentinelProperty interface is responsible for triggering PropertyListener
    // a callback to the configUpdate method
    protected final SentinelProperty<T> property;

    public AbstractDataSource(Converter<S, T> parser) {
        if (parser == null) {
            throw new IllegalArgumentException("parser can't be null");
        }
        this.parser = parser;
        this.property = new DynamicSentinelProperty<T>();
    }
    @Override
    public T loadConfig(a) throws Exception {
        return loadConfig(readSource());
    }
    public T loadConfig(S conf) throws Exception {
        return parser.convert(conf);
    }
    @Override
    public SentinelProperty<T> getProperty(a) {
        returnproperty; }}Copy the code

In fact, each concrete DataSource implementation class needs to do three things:

  • Implement the readSource method to convert the raw data in the data source into data S that we can process
  • Provide a Converter to convert data S to the final data T
  • Update the final data T to the specific RuleManager

I’ve condensed the full flow of how rules are loaded from the data source into RuleManager into this diagram:

You can look at this figure against the source code, it is easy to understand the process, here I will not expand the specific source code, there are a few points to pay attention to is:

  • The persistence configuration center of the rule can be redis, NACos, ZK, File, and any data source that can be persisted, as long as the client can be notified when the rule is updated
  • Rules can be updated through Sentinel Dashboard or through each configuration center’s own update interface
  • AbstractDataSource SentinelProperty holds an PropertyListener interface, and it is the PropertyListener that ultimately updates the rules in RuleManager

Rule persistence

Now that we know how it works, let’s explain how to access the persistence of rules.

At present, Sentinel implements 5 rule persistence methods by default, which are file, Redis, NACOS, ZK and Apollo.

Let’s take a look at each of the five methods, using persistent traffic limiting rules as an example.

File

One of the problems with file persistence is that unlike other configuration centers, files are notified when data changes occur. Using file persistence requires us to periodically scan the file to see if it has detected any changes.

File data source is achieved by FileRefreshableDataSource class, he is through the file last updated time to determine whether the rules are changed.

First we need to introduce dependencies:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
    <version>x.y.z</version>
</dependency>
Copy the code

The access method is as follows:

private void init(a) throws Exception {
	// The address of the file that holds the limiting rules
	String flowRuleName = yourFlowRuleFileName();
	Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    // Create a file rule data source
    FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(flowRuleName, parser);
    // Register the Property with RuleManager
    FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
Copy the code

PS: Note that we need to call the data source registered method when the system starts, otherwise it will not take effect. You can use Spring to initialize the method, or you can customize a class that implements the InitFunc interface in Sentinel.

Sentinel scans the InitFunc implementation class via SPI at startup time and executes InitFunc’s init method, so this is also a viable option to try if your system is not using Spring.

Redis

The implementation class for Redis data source is RedisDataSource.

First we introduce dependencies:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-redis</artifactId>
    <version>x.y.z</version>
</dependency>
Copy the code

The access method is as follows:

private void init(a) throws Exception {
	String redisHost = yourRedisHost();
	String redisPort = yourRedisPort();
    String ruleKey = yourRuleKey();
    String channel = yourChannel();
	Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    RedisConnectionConfig config = RedisConnectionConfig.builder()
        .withHost(redisHost)
        .withPort(redisPort)
        .build();
    ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<>(config, ruleKey, channel, parser);
    FlowRuleManager.register2Property(redisDataSource.getProperty());
}
Copy the code

Nacos

The implementation class for the Nacos data source is NacosDataSource.

First we introduce dependencies:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>x.y.z</version>
</dependency>
Copy the code

The access method is as follows:

private void init(a) throws Exception {
	String remoteAddress = yourRemoteAddress();
	String groupId = yourGroupId();
    String dataId = yourDataId();
	Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> nacosDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser);
    FlowRuleManager.register2Property(nacosDataSource.getProperty());
}
Copy the code

Zk

The implementation class of the Zk data source is ZookeeperDataSource.

First we introduce dependencies:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-zookeeper</artifactId>
    <version>x.y.z</version>
</dependency>
Copy the code

The access method is as follows:

private void init(a) throws Exception {
	String remoteAddress = yourRemoteAddress();
	String path = yourPath();
	Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> zookeeperDataSource = new ZookeeperDataSource<>(remoteAddress, path, parser);
    FlowRuleManager.register2Property(zookeeperDataSource.getProperty());
}
Copy the code

Apollo

The implementation class for Apollo data source is ApolloDataSource.

First we introduce dependencies:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-apollo</artifactId>
    <version>x.y.z</version>
</dependency>
Copy the code

The access method is as follows:

private void init(a) throws Exception {
	String namespaceName = yourNamespaceName();
	String ruleKey = yourRuleKey();
    String defaultRules = yourDefaultRules();
	Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> apolloDataSource = new ApolloDataSource<>(namespaceName, ruleKey, path, defaultRules);
    FlowRuleManager.register2Property(apolloDataSource.getProperty());
}
Copy the code

It can be seen that the way of persistence in 5 is basically the same with minor differences. It is mainly connected to each configuration center to realize data conversion and monitor data changes in the configuration center. After receiving data changes, it is ok to update the latest rules to RuleManager in time.

For more original articles, please pay attention to the public account “Hou Yi Code by Code”