一、引子
前面的介紹過插槽鏈的NodeSelectorSlot和ClusterBuilderSlot,見Sentinel之Slots插槽源碼分析(一)這篇文章灭必。
還介紹了LogSlot和StatisticSlot妹笆,見Sentinel之Slots插槽源碼分析(二)這篇文章。
接下來(lái)我們開始分析SystemSlot浮创。
二、SystemSlot
SystemSlot主要是用來(lái)系統(tǒng)規(guī)則的檢查,包括平均RT忆谓,qps,線程數(shù)踱承,系統(tǒng)負(fù)載(只是針對(duì)linux系統(tǒng))倡缠。
下面具體分析SystemSlot是如何實(shí)現(xiàn)系統(tǒng)檢查的。
2.1 SystemSlot類
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
SystemRuleManager.checkSystem(resourceWrapper);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
}
可以看到調(diào)用了SystemRuleManager.的checkSystem方法茎活,到SystemRuleManager類中去昙沦。
2.2 SystemRuleManager類
先看定義的變量信息:
private static volatile double highestSystemLoad = Double.MAX_VALUE;
private static volatile double qps = Double.MAX_VALUE;
private static volatile long maxRt = Long.MAX_VALUE;
private static volatile long maxThread = Long.MAX_VALUE;
/**
* mark whether the threshold are set by user.
*/
private static volatile boolean highestSystemLoadIsSet = false;
private static volatile boolean qpsIsSet = false;
private static volatile boolean maxRtIsSet = false;
private static volatile boolean maxThreadIsSet = false;
private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);
private static SystemStatusListener statusListener = null;
private final static SystemPropertyListener listener = new SystemPropertyListener();
private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();
private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-system-status-record-task", true));
- highestSystemLoad(系統(tǒng)負(fù)載)、qps载荔、maxRt盾饮、maxThread是定義的審計(jì)指標(biāo)值,默認(rèn)值設(shè)置了最大值懒熙。
- highestSystemLoadIsSet丘损、qpsIsSet、maxRtIsSet工扎、maxThreadIsSet用來(lái)標(biāo)是否被設(shè)置過徘钥。
- checkSystemStatus一個(gè)原子變量,規(guī)則檢查標(biāo)識(shí)肢娘,如果規(guī)則被更新了呈础,則會(huì)設(shè)置為true。
- statusListener:系統(tǒng)狀態(tài)的監(jiān)聽器橱健,通過線程任務(wù)啟動(dòng)而钞,如果系統(tǒng)負(fù)載大于設(shè)置的負(fù)載,則會(huì)記錄日志信息拘荡。
- listener:實(shí)際上是一個(gè)觀察者笨忌,用來(lái)監(jiān)聽規(guī)則是否變化,若變化則進(jìn)行更新俱病。
- currentProperty:實(shí)際上是具體的主題DynamicSentinelProperty(這里用的是觀察者模式的思想解釋的??)官疲。
- scheduler:定義了是定時(shí)任務(wù)線程池,線程池中只有一個(gè)線程亮隙,這個(gè)線程就是用來(lái)處理statusListener的途凫。
在SystemRuleManager初次加載時(shí),會(huì)執(zhí)行static靜態(tài)快了代碼:
static {
checkSystemStatus.set(false);
statusListener = new SystemStatusListener();
scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
currentProperty.addListener(listener);
}
- 設(shè)置checkSystemStatus為false溢吻。
- 創(chuàng)建了一個(gè)SystemStatusListener维费,并把statusListener添加到線程池中果元。
- currentProperty中增加一個(gè)觀察者listener。
SystemRuleManager是System規(guī)則檢查的核心代碼犀盟,下面繼續(xù)看完代碼而晒,再分析系統(tǒng)規(guī)則如何檢測(cè)的。
/**
* Listen to the {@link SentinelProperty} for {@link SystemRule}s. The property is the source
* of {@link SystemRule}s. System rules can also be set by {@link #loadRules(List)} directly.
*
* @param property the property to listen.
*/
public static void register2Property(SentinelProperty<List<SystemRule>> property) {
synchronized (listener) {
currentProperty.removeListener(listener);
property.addListener(listener);
currentProperty = property;
}
}
/**
* Load {@link SystemRule}s, former rules will be replaced.
*
* @param rules new rules to load.
*/
public static void loadRules(List<SystemRule> rules) {
currentProperty.updateValue(rules);
}
public static List<SystemRule> getRules() {
List<SystemRule> result = new ArrayList<SystemRule>();
if (!checkSystemStatus.get()) {
return result;
}
if (highestSystemLoadIsSet) {
SystemRule loadRule = new SystemRule();
loadRule.setHighestSystemLoad(highestSystemLoad);
result.add(loadRule);
}
if (maxRtIsSet) {
SystemRule rtRule = new SystemRule();
rtRule.setAvgRt(maxRt);
result.add(rtRule);
}
if (maxThreadIsSet) {
SystemRule threadRule = new SystemRule();
threadRule.setMaxThread(maxThread);
result.add(threadRule);
}
if (qpsIsSet) {
SystemRule qpsRule = new SystemRule();
qpsRule.setQps(qps);
result.add(qpsRule);
}
return result;
}
- register2Property方法:該方法其實(shí)就是把listener觀察者注冊(cè)到一個(gè)新的主題上阅畴,這里會(huì)在動(dòng)態(tài)數(shù)據(jù)源的時(shí)候用到倡怎,比如把系統(tǒng)的規(guī)則寫入到redis,zookeeper中等贱枣。
- loadRules方法:通過currentProperty.updateValue方法更新系統(tǒng)的規(guī)則設(shè)置监署。
- getRules:獲取系統(tǒng)的規(guī)則,這里只要當(dāng)規(guī)則狀態(tài)checkSystemStatus是ture且設(shè)置過的系統(tǒng)的規(guī)則后才會(huì)獲取到值纽哥。
接收到系統(tǒng)動(dòng)態(tài)規(guī)則后钠乏,通過SystemPropertyListener的configUpdate更新規(guī)則,下面看下System的觀察者代碼是怎么定義的春塌。
2.2.1 SystemPropertyListener
SystemPropertyListener是SystemRuleManager的靜態(tài)內(nèi)部類晓避。
static class SystemPropertyListener extends SimplePropertyListener<List<SystemRule>> {
@Override
public void configUpdate(List<SystemRule> rules) {
restoreSetting();
// systemRules = rules;
if (rules != null && rules.size() >= 1) {
for (SystemRule rule : rules) {
loadSystemConf(rule);
}
} else {
checkSystemStatus.set(false);
}
RecordLog.info(String.format("[SystemRuleManager] Current system check status: %s, highestSystemLoad: "
+ highestSystemLoad + ", " + "maxRt: %d, maxThread: %d, maxQps: " + qps, checkSystemStatus.get(), maxRt, maxThread));
}
protected void restoreSetting() {
checkSystemStatus.set(false);
// should restore changes
highestSystemLoad = Double.MAX_VALUE;
maxRt = Long.MAX_VALUE;
maxThread = Long.MAX_VALUE;
qps = Double.MAX_VALUE;
highestSystemLoadIsSet = false;
maxRtIsSet = false;
maxThreadIsSet = false;
qpsIsSet = false;
}
}
SystemPropertyListener繼承SimplePropertyListener方法,SimplePropertyListener又實(shí)現(xiàn)的PropertyListener的configLoad方法只壳。
PropertyListener是一個(gè)抽象觀察接口够滑,定義了configUpdate和configLoad方法。
configUpdate方法就是具體的更新方法吕世。
所以SystemPropertyListener只要實(shí)現(xiàn)configUpdate方法即可彰触。在configUpdate方法中:
- restoreSetting方法:會(huì)重置前面介紹的9個(gè)變量值,方便規(guī)則數(shù)據(jù)更新命辖。
- loadSystemConf方法:設(shè)置規(guī)則,看下面代碼
public static void loadSystemConf(SystemRule rule) {
boolean checkStatus = false;
// Check if it's valid.
if (rule.getHighestSystemLoad() >= 0) {
highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());
highestSystemLoadIsSet = true;
checkStatus = true;
}
if (rule.getAvgRt() >= 0) {
maxRt = Math.min(maxRt, rule.getAvgRt());
maxRtIsSet = true;
checkStatus = true;
}
if (rule.getMaxThread() >= 0) {
maxThread = Math.min(maxThread, rule.getMaxThread());
maxThreadIsSet = true;
checkStatus = true;
}
if (rule.getQps() >= 0) {
qps = Math.min(qps, rule.getQps());
qpsIsSet = true;
checkStatus = true;
}
checkSystemStatus.set(checkStatus);
}
可以看到該方法就是設(shè)置highestSystemLoad况毅、maxRt、maxThread尔艇、qps尔许;并且設(shè)置對(duì)應(yīng)的設(shè)置標(biāo)志位true。
上述代碼講了這么多其實(shí)就是為了說明系統(tǒng)規(guī)則判定時(shí)终娃,這些指標(biāo)的規(guī)則是如何設(shè)置的味廊,有了這些規(guī)則值就可以通過checkSystem方法來(lái)進(jìn)行系統(tǒng)保護(hù)了。
public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
// Ensure the checking switch is on.
if (!checkSystemStatus.get()) {
return;
}
// for inbound traffic only
if (resourceWrapper.getType() != EntryType.IN) {
return;
}
// total qps
double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
if (currentQps > qps) {
throw new SystemBlockException(resourceWrapper.getName(), "qps");
}
// total thread
int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
if (currentThread > maxThread) {
throw new SystemBlockException(resourceWrapper.getName(), "thread");
}
double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
if (rt > maxRt) {
throw new SystemBlockException(resourceWrapper.getName(), "rt");
}
// BBR algorithm.
if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
if (currentThread > 1 &&
currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
throw new SystemBlockException(resourceWrapper.getName(), "load");
}
}
}
- checkSystemStatus,如果該狀態(tài)是false是棠耕,說明規(guī)則沒有設(shè)置過余佛,直接返回。
- 如果resourceWrapper的type不是IN的窍荧,說明資源保護(hù)不是入境調(diào)用辉巡,直接返回。
- 拿到全局節(jié)點(diǎn)ENTRY_NODE蕊退,并拿到請(qǐng)求成功數(shù)值郊楣,線程數(shù)憔恳,平均rt的值。
- 依次比較qps净蚤,thread钥组,rt,load今瀑;若這些有大于系統(tǒng)規(guī)則的設(shè)置值程梦,則拋出SystemBlockException異常。
- load比較需要先獲取當(dāng)前系統(tǒng)的負(fù)載放椰,并通過BBR算法比較系統(tǒng)負(fù)載是否超標(biāo)。有關(guān)BBR算法可以參考網(wǎng)上文章愉粤。
三砾医、我的總結(jié)
1、SystemSlot插槽是整個(gè)插槽鏈規(guī)則校驗(yàn)的第一個(gè)衣厘,用于系統(tǒng)規(guī)則設(shè)置的校驗(yàn)如蚜。
2、系統(tǒng)規(guī)則的設(shè)置通過loadRules方法直接設(shè)置影暴,但是通常生產(chǎn)環(huán)境使用時(shí)错邦,會(huì)有一個(gè)動(dòng)態(tài)數(shù)據(jù)源的接入。
3型宙、系統(tǒng)設(shè)置規(guī)則的變更用到觀察者模式的思想撬呢。
4、系統(tǒng)負(fù)載檢測(cè)用到了BBR的算法妆兑。
5魂拦、系統(tǒng)rt,qps搁嗓,thread的統(tǒng)計(jì)數(shù)據(jù)保存在全局節(jié)點(diǎn)ENTRY_NODE中芯勘。
以上內(nèi)容,若有不當(dāng)之處腺逛,請(qǐng)指正