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

AutoRefreshDataSource

public AutoRefreshDataSource(Converter<S, T> configParser, final long recommendRefreshMs) { super(configParser); if (recommendRefreshMs <= 0) { throw new IllegalArgumentException("recommendRefreshMs must > 0, but " + recommendRefreshMs + " get"); } this.recommendRefreshMs = recommendRefreshMs; startTimerService(); }

AutoRefreshDataSource的构造器一开始会调用父类的构造器进行初始化,如下:
AbstractDataSource

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

AbstractDataSource的构造器是为了给两个变量设值parser和property,其中property是DynamicSentinelProperty的实例。

我们再回到AutoRefreshDataSource中,AutoRefreshDataSource设值完recommendRefreshMs参数后会调用startTimerService方法来开启一个定时的调度任务。
AutoRefreshDataSource#startTimerService

private void startTimerService() { service = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-datasource-auto-refresh-task", true)); service.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { if (!isModified()) { return; } T newValue = loadConfig(); getProperty().updateValue(newValue); } catch (Throwable e) { RecordLog.info("loadConfig exception", e); } } }, recommendRefreshMs, recommendRefreshMs, TimeUnit.MILLISECONDS); } public SentinelProperty<T> getProperty() { return property; }

这个方法里面会开启一个线程,每3000ms调用一次run方法。run方法里会首先会校验一下文件有没有被修改过,如果有的话就调用loadConfig来加载配置,然后调用getProperty方法获取父类设置的property来更新配置。
下来我们依次来讲解一下这几个主要的方法:

isModified方法是一个钩子,调用的是FileRefreshableDataSource的isModified方法:
FileRefreshableDataSource#isModified

protected boolean isModified() { long curLastModified = file.lastModified(); if (curLastModified != this.lastModified) { this.lastModified = curLastModified; return true; } return false; }

isModified每次都会查看file有没有被修改,并记录一下修改的时间。

接着往下是调用loadConfig加载文件:
AbstractDataSource#loadConfig

public T loadConfig() throws Exception { return loadConfig(readSource()); } public T loadConfig(S conf) throws Exception { T value = parser.convert(conf); return value; }

FileRefreshableDataSource#readSource

public String readSource() throws Exception { if (!file.exists()) { // Will throw FileNotFoundException later. RecordLog.warn(String.format("[FileRefreshableDataSource] File does not exist: %s", file.getAbsolutePath())); } FileInputStream inputStream = null; try { inputStream = new FileInputStream(file); FileChannel channel = inputStream.getChannel(); if (channel.size() > buf.length) { throw new IllegalStateException(file.getAbsolutePath() + " file size=" + channel.size() + ", is bigger than bufSize=" + buf.length + ". Can't read"); } int len = inputStream.read(buf); return new String(buf, 0, len, charset); } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception ignore) { } } } }

loadConfig方法的实现还是很清晰的,首先是调用readSource通过io流读取文件,然后再通过传入的解析器解析文件的内容。

接着会调用DynamicSentinelProperty的updateValue方法,遍历监听器更新配置:
DynamicSentinelProperty#updateValue

public boolean updateValue(T newValue) { //判断新的元素和旧元素是否相同 if (isEqual(value, newValue)) { return false; } RecordLog.info("[DynamicSentinelProperty] Config will be updated to: " + newValue); value = newValue; for (PropertyListener<T> listener : listeners) { listener.configUpdate(newValue); } return true; }

当然,还没加载FlowRuleManager的时候肯定是没有监听器的。

讲完了FileRefreshableDataSource的父类的加载,我们再回到FileRefreshableDataSource的构造器中。继续往下走会调用firstLoad方法首次加载配置文件初始化一次。
FileRefreshableDataSource#firstLoad

private void firstLoad() { try { T newValue = loadConfig(); getProperty().updateValue(newValue); } catch (Throwable e) { RecordLog.info("loadConfig exception", e); } }

下面我们再看一下FlowRuleManager是怎么注册的。注册的时候会调用register2Property方法进行注册:

FlowRuleManager#register2Property

public static void register2Property(SentinelProperty<List<FlowRule>> property) { AssertUtil.notNull(property, "property cannot be null"); synchronized (LISTENER) { RecordLog.info("[FlowRuleManager] Registering new property to flow rule manager"); currentProperty.removeListener(LISTENER); property.addListener(LISTENER); currentProperty = property; } }

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

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