6.Sentinel源码分析—Sentinel是如何动态加载配置限流的?

Sentinel源码解析系列:
1.Sentinel源码分析—FlowRuleManager加载规则做了什么?

2. Sentinel源码分析—Sentinel是如何进行流量统计的?

3. Sentinel源码分析— QPS流量控制是如何实现的?

4.Sentinel源码分析— Sentinel是如何做到降级的?

5.Sentinel源码分析—Sentinel如何实现自适应限流?

有时候我们做限流的时候并不想直接写死在代码里面,然后每次要改规则,或者增加规则的时候只能去重启应用来解决。而是希望能够动态的更改配置,这样万一出现紧急情况还能动态的进行配置修改。例如2018年的双十一,淘宝的其他服务没有一点问题,万万没想到在前几分钟购物车服务挂了,这个时候就可以紧急限流,对应用进行拯救。

其实看完前面的内容,对动态配置应该是水到渠成的事情,因为所有的配置修改都是通过限流管理器如FlowRuleManager的内部监听器来实现的,所以只要动态的给监听器信号,那么就可以做到动态的修改配置。

接下来我们来看看Sentinel是怎么做的。一般的情况下,动态配置常见的实现方式有两种:

拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;

推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。

而Sentinel目前两种都支持:

Pull-based: 文件、Consul (since 1.7.0)

Push-based: ZooKeeper, Redis, Nacos, Apollo

由于支持的方式太多,我这里只讲解两种,文件和ZooKeeper,分别对应推拉两种模式。

Pull-based: 文件

首先上个例子:
FlowRule.json

[ { "resource": "abc", "controlBehavior": 0, "count": 20.0, "grade": 1, "limitApp": "default", "strategy": 0 }, { "resource": "abc1", "controlBehavior": 0, "count": 20.0, "grade": 1, "limitApp": "default", "strategy": 0 } ]

SimpleFileDataSourceDemo:

public class SimpleFileDataSourceDemo { private static final String KEY = "abc"; public static void main(String[] args) throws Exception { SimpleFileDataSourceDemo simpleFileDataSourceDemo = new SimpleFileDataSourceDemo(); simpleFileDataSourceDemo.init(); Entry entry = null; try { entry = SphU.entry(KEY); // dosomething } catch (BlockException e1) { // dosomething } catch (Exception e2) { // biz exception } finally { if (entry != null) { entry.exit(); } } } private void init() throws Exception { String flowRulePath = "/Users/luozhiyun/Downloads/test/FlowRule.json"; // Data source for FlowRule FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>( flowRulePath, flowRuleListParser); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); } private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}); }

这个例子主要就是写死一个资源文件,然后读取资源文件里面的内容,再通过自定义的资源解析器来解析文件的内容后设置规则。

这里我们主要需要分析FileRefreshableDataSource是怎么加载文件然后通过FlowRuleManager注册的。

FileRefreshableDataSource继承关系:

6.Sentinel源码分析—Sentinel是如何动态加载配置限流的?

FileRefreshableDataSource

private static final int MAX_SIZE = 1024 * 1024 * 4; private static final long DEFAULT_REFRESH_MS = 3000; private static final int DEFAULT_BUF_SIZE = 1024 * 1024; private static final Charset DEFAULT_CHAR_SET = Charset.forName("utf-8"); public FileRefreshableDataSource(String fileName, Converter<String, T> configParser) throws FileNotFoundException { this(new File(fileName), configParser, DEFAULT_REFRESH_MS, DEFAULT_BUF_SIZE, DEFAULT_CHAR_SET); } public FileRefreshableDataSource(File file, Converter<String, T> configParser, long recommendRefreshMs, int bufSize, Charset charset) throws FileNotFoundException { super(configParser, recommendRefreshMs); if (bufSize <= 0 || bufSize > MAX_SIZE) { throw new IllegalArgumentException("bufSize must between (0, " + MAX_SIZE + "], but " + bufSize + " get"); } if (file == null || file.isDirectory()) { throw new IllegalArgumentException("File can't be null or a directory"); } if (charset == null) { throw new IllegalArgumentException("charset can't be null"); } this.buf = new byte[bufSize]; this.file = file; this.charset = charset; // If the file does not exist, the last modified will be 0. this.lastModified = file.lastModified(); firstLoad(); }

FileRefreshableDataSource的构造器里面会设置各种参数,如:缓冲区大小、字符编码、文件上次的修改时间、文件定时刷新时间等。
这个方法会调用父类的构造器进行初始化,我们再看一下AutoRefreshDataSource做了什么。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwdpz.html