センチネルソース解決シリーズ:
1.Sentinelソースコード解析-FlowRuleManagerロードルールが何をしましたか?
2.センチネルのソースコード解析-Sentinelはどのようにトラフィック統計のですか?
3.センチネルソースコード解析 - QPSトラフィック制御を達成する方法ですか?
4.Sentinelソースコード解析 - センチネルダウングレードを行う方法ですか?
どのように5.Sentinelのソースコード解析は、電流制限を適応-Sentinel?
時々、私たちは、時間コード化された内部コードに直接の流れを制限したくない、その後、毎回あなたがルールを変更したい、またはルールのみを解決することができ、アプリケーションを再起動するための時間が増加します。しかし、緊急時に動的に変更設定することができるように、動的に設定を変更できるようにします。このような二から一一淘宝網、他のサービスとしてではない2018小さな問題は、最初の数分のショッピングカートサービスをハングアップすることが期待されることはありません、この時間は、それはアプリケーションを救うために、緊急時の流れを制限することができます。
FlowRuleManager内部リスナーを達成するように、すべての設定変更は、絞りマネージャを介してリスナーに動的信号限りあるため、実際には、上記を読んだ後に、動的設定は、天然のものであるべきで、それがすることができます構成を動的に変更を行います。
私たちは歩哨を見て次は何をするかです。通常の状況下では、動的な構成は、2つの一般的な実装があります。
- モードを引い:クライアントが定期的にポーリングルールを引っ張っルール管理センターへの取り組みを、このルールはそうでRDBMS、ファイルの中心部、さらにはVCSであってもよいです。これを行う方法は、欠点がタイムリーに変更を取得することができません、簡単です。
- プッシュモード:統一されたルールのためのセンターでは、そのような使用などの変更、ナコス、飼育係や他の流通センターに聞いて登録されたリスナーを経由してクライアントにプッシュします。このアプローチは、より良いリアルタイム性と一貫性を確保しています。
これらの両方Sentinelは、現在サポートされています。
- プル・ベース:(1.7.0以降)ファイル、領事を
- プッシュベース:ZooKeeperの、Redisの、ナコス、アポロ
だから、多くの支援の方法は、私はプッシュプルモードに対応し、ファイルとのZooKeeperの2種類を説明するためにここにいます。
プル・ベース:ファイル
まず、最後の例:
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継承:
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がのが何が行われたかを見てみましょう初期化します。
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構成はプロパティは、例えばDynamicSentinelPropertyである、値パーサとプロパティに設定されています。
我々は戻ってAutoRefreshDataSource、recommendRefreshMsパラメータの値を設定した後AutoRefreshDataSource後、時限スケジュールタスクを開くために呼び出し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;
}
スレッドを開きます。この方法では、runメソッドは、一度3000msと呼ばれています。あなたは、コンフィギュレーションをロードするためにloadConfigを呼び出して設定を更新するように設定されている親クラスのプロパティを取得するためのgetPropertyメソッドを呼び出した場合のレーンは、最初のファイルが変更されていないかどうか確認するメソッドを実行します。
次に我々は、これらの主要なアプローチを説明するために回します:
isModifiedメソッドは、呼び出しがFileRefreshableDataSource isModifiedメソッドで、フックです:
FileRefreshableDataSource#のisModified
protected boolean isModified() {
long curLastModified = file.lastModified();
if (curLastModified != this.lastModified) {
this.lastModified = curLastModified;
return true;
}
return false;
}
isModifiedビューファイルは、すべての時間が変更されず、変更され、それを記録します。
:ファイルのロード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によって読み取られ、着信ファイルパーサーの内容を通じて呼び出すことで、非常に明確です。
そして、updateValueメソッドDynamicSentinelPropertyは、構成を更新するリスナーを歩いて、呼び出します。
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;
}
}
この方法は、実際にリスナーを追加して、作成したプロパティflowRuleDataSourceにcurrentPropertyのFlowRuleManagerを置き換えています。そして、スレッド内のflowRuleDataSourceタイミングは、このように動的更新ルールを達成、これについては3秒ごとにconfigupdateのメソッドリスナーリフレッシュルールを呼び出します。
プッシュベース:ZooKeeperの
私たちは、最初の例を与えます:
public static void main(String[] args) {
final String remoteAddress = "127.0.0.1:2181";
final String path = "/Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ZookeeperDataSource<>(remoteAddress, path,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
ここで私は定義し/Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
、このパスの内容が変更された場合、それはルールを更新しますが、このパスを。
私たちは、ZookeeperDataSourceの継承を見て:
ZookeeperDataSource
public ZookeeperDataSource(final String serverAddr, final String path, Converter<String, T> parser) {
super(parser);
if (StringUtil.isBlank(serverAddr) || StringUtil.isBlank(path)) {
throw new IllegalArgumentException(String.format("Bad argument: serverAddr=[%s], path=[%s]", serverAddr, path));
}
this.path = path;
init(serverAddr, null);
}
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>();
}
ZookeeperDataSourceは、最初のチェックが完了した後にinitメソッドを呼び出して、親クラスのパラメータセットを呼び出します。
ZookeeperDataSource#のinit
private void init(final String serverAddr, final List<AuthInfo> authInfos) {
initZookeeperListener(serverAddr, authInfos);
loadInitialConfig();
}
ZookeeperDataSource#initZookeeperListener
private void initZookeeperListener(final String serverAddr, final List<AuthInfo> authInfos) {
try {
//设置监听
this.listener = new NodeCacheListener() {
@Override
public void nodeChanged() {
try {
T newValue = loadConfig();
RecordLog.info(String.format("[ZookeeperDataSource] New property value received for (%s, %s): %s",
serverAddr, path, newValue));
// Update the new value to the property.
getProperty().updateValue(newValue);
} catch (Exception ex) {
RecordLog.warn("[ZookeeperDataSource] loadConfig exception", ex);
}
}
};
String zkKey = getZkKey(serverAddr, authInfos);
if (zkClientMap.containsKey(zkKey)) {
this.zkClient = zkClientMap.get(zkKey);
} else {
//如果key不存在,那么就加锁设值
synchronized (lock) {
if (!zkClientMap.containsKey(zkKey)) {
CuratorFramework zc = null;
//根据不同的条件获取client
if (authInfos == null || authInfos.size() == 0) {
zc = CuratorFrameworkFactory.newClient(serverAddr, new ExponentialBackoffRetry(SLEEP_TIME, RETRY_TIMES));
} else {
zc = CuratorFrameworkFactory.builder().
connectString(serverAddr).
retryPolicy(new ExponentialBackoffRetry(SLEEP_TIME, RETRY_TIMES)).
authorization(authInfos).
build();
}
this.zkClient = zc;
this.zkClient.start();
Map<String, CuratorFramework> newZkClientMap = new HashMap<>(zkClientMap.size());
newZkClientMap.putAll(zkClientMap);
newZkClientMap.put(zkKey, zc);
zkClientMap = newZkClientMap;
} else {
this.zkClient = zkClientMap.get(zkKey);
}
}
}
//为节点添加watcher
//监听数据节点的变更,会触发事件
this.nodeCache = new NodeCache(this.zkClient, this.path);
this.nodeCache.getListenable().addListener(this.listener, this.pool);
this.nodeCache.start();
} catch (Exception e) {
RecordLog.warn("[ZookeeperDataSource] Error occurred when initializing Zookeeper data source", e);
e.printStackTrace();
}
}
このメソッドは、主にクライアントの値を作成し、監視を設定するために使用され、従来の操作ZK、なじみのない、あなたが行くことができると学芸員が使用されている方法を見ています。
private void loadInitialConfig() {
try {
//调用父类的loadConfig方法
T newValue = loadConfig();
if (newValue == null) {
RecordLog.warn("[ZookeeperDataSource] WARN: initial config is null, you may have to check your data source");
}
getProperty().updateValue(newValue);
} catch (Exception ex) {
RecordLog.warn("[ZookeeperDataSource] Error when loading initial config", ex);
}
}
UpdateValueは、情報ノードが最初にロードされ、ZKと監視値を設定した後、クライアントの後に一度呼び出さ。
AbstractDataSource
public T loadConfig() throws Exception {
return loadConfig(readSource());
}
public T loadConfig(S conf) throws Exception {
T value = parser.convert(conf);
return value;
}
LoadConfigの親クラスはreadSourceは、構成情報を読み取り、その後、デシリアライズのparser.convertを呼び出すサブクラスを呼び出します。
ZookeeperDataSource#readSource
public String readSource() throws Exception {
if (this.zkClient == null) {
throw new IllegalStateException("Zookeeper has not been initialized or error occurred");
}
String configInfo = null;
ChildData childData = nodeCache.getCurrentData();
if (null != childData && childData.getData() != null) {
configInfo = new String(childData.getData());
}
return configInfo;
}
この方法は、ノードZK内の情報を読み取るために使用されます。
動的コンフィギュレーションファイルに上記の最後の方法FlowRuleManager.register2Propertyは同じです。