ようこそgithub.com/hsfxuebaoに注目して ください , 参考になれば幸いです. 可能だと思われる場合は、スターをクリックしてください.
1.システムスロット
Sentinel システムのアダプティブ電流制限は、アダプティブ フロー制御戦略を通じて、アプリケーションの負荷、CPU 使用率、全体の平均 RT、イングレス QPS、同時スレッド数などのいくつかのディメンションの監視指標と組み合わせて、全体的なディメンションからアプリケーションのイングレス トラフィックを制御します。 、让系统的入口流量和系统的负载达到一个平衡
、システムの全体的な安定性を確保しながら、システムが可能な限り最大のスループットで実行できるようにします。
Sentinel システムの適応電流制限は実装を指しTCP BBR
、システム負荷を介して電流を制限するのではなく、システムが処理できる要求と着信要求に従ってバランスをとります。その最終的な目標は、システムが圧倒されるのを防ぐことです。 、負荷が特定のしきい値よりも低くなければならないのではなく、システムのスループットを向上させます
1.1 システム規則
システム保護ルールは、アプリケーション レベルのイングレス トラフィックから制御され、アプリケーション インジケーターは、単一マシンの負荷、CPU 使用率、平均 RT、イングレス QPS、同時スレッド数などの複数の次元から監視されるため、システムは、システム全体の安定性を確保しながら、可能な限り最大のスループットで実行できます。
整体维度
リソースの次元ではなく、システム保護ルールが適用され仅对入口流量生效
ます。イングレス トラフィックとは、Web サービスや Dubbo サーバーによって受信されるリクエストなど、アプリケーションに入るトラフィック ( ) を指しEntryType.IN
、これらはすべてイングレス トラフィックです。
索引 | 例証する |
---|---|
ロード アダプティブ (Linux/Unix 系のマシンでのみ有効) | システムの load1 は、適応型システム保護を実行するためのヒューリスティック インデックスとして使用されます。システム負荷 1 が設定されたヒューリスティック値を超え、システムの現在の同時スレッド数が推定システム容量を超えると、システム保護 (BBR フェーズ) がトリガーされます。システム容量は、システムの maxQps * minRt によって推定されます。設定基準値は一般的にCPUコア数×2.5です。 |
CPU 使用率 (バージョン 1.5.0+) | システムの CPU 使用率がしきい値を超えると、より敏感なシステム保護がトリガーされます (値の範囲は 0.0 ~ 1.0)。 |
平均RT | システム保護は、1 台のマシン上のすべてのイングレス トラフィックの平均 RT がしきい値 (ミリ秒単位) に達するとトリガーされます。 |
同時スレッド数 | システム保護は、1 台のマシン上のすべてのイングレス トラフィックの同時スレッド数がしきい値に達するとトリガーされます。 |
イングレス QPS | システム保護は、1 台のマシン上のすべてのイングレス トラフィックの QPS がしきい値に達するとトリガーされます。 |
1.2 原則
システムがリクエストを処理するプロセスを水道管に例えます. 入ってくるリクエストはこの水道管に水を注ぐことです. システムがスムーズに処理されている場合, リクエストはキューイングされる必要はなく, 水道管を直接通過します.このリクエストの RT が最短です; それ以外の場合、リクエストが積み重なった場合、リクエストの処理時間は になります排队时间 + 最短处理时间
。
水道管内の水量を確保し、スムーズに水を流すことができれば、キューイングリクエストが増えることはなく、このときのシステム負荷がこれ以上悪化することはありません。
T
(水道管内の水の量) を表すために使用RT
し、リクエストの処理時間を表すために使用し、P を使用して着信リクエストの数を表すと、水道管に入ってから水から出てくるまでのリクエストパイプ、この水道管にP * RT
はリクエストがあります。つまり、 が のときT ≈ QPS * Avg(RT)
は、システムの処理能力と受信リクエスト数が釣り合っていると考えられ、システムの負荷はこれ以上悪化しません。
次の問題は、水道管の水位が平衡点に達する可能性があることですが、この平衡点は、水道管の水位が上昇し続けないことを保証することしかできませんが、まだ問題があります。平衡点に達すると、水道管はどれだけの水を蓄積したか. 配水管内の水がすでに一定の桁数になっている場合、この時点でシステムが許容する水の量はゆっくりとしか流れない可能性があり、RT は大きくなり、配水管に蓄積された水は残ります。システムの処理能力を浪費します。
入口での流量を水道管から出る流量の最大値に保つと、水道管の処理能力を最大化できます。
1.3 適応電流制限の使用例
public class SystemGuardDemo {
private static AtomicInteger pass = new AtomicInteger();
private static AtomicInteger block = new AtomicInteger();
private static AtomicInteger total = new AtomicInteger();
private static volatile boolean stop = false;
private static final int threadCount = 100;
private static int seconds = 60 + 40;
public static void main(String[] args) throws Exception {
tick();
//初始化规则参数
initSystemRule();
//开启线程执行流量调用
for (int i = 0; i < threadCount; i++) {
Thread entryThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Entry entry = null;
try {
entry = SphU.entry("methodA", EntryType.IN);
pass.incrementAndGet();
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
// ignore
}
} catch (BlockException e1) {
block.incrementAndGet();
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
// ignore
}
} catch (Exception e2) {
// biz exception
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
}
}
});
entryThread.setName("working-thread");
entryThread.start();
}
}
private static void initSystemRule() {
List<SystemRule> rules = new ArrayList<SystemRule>();
SystemRule rule = new SystemRule();
// max load is 3
rule.setHighestSystemLoad(3.0);
// max cpu usage is 60%
rule.setHighestCpuUsage(0.6);
// max avg rt of all request is 10 ms
rule.setAvgRt(10);
// max total qps is 20
rule.setQps(20);
// max parallel working thread is 10
rule.setMaxThread(10);
rules.add(rule);
SystemRuleManager.loadRules(Collections.singletonList(rule));
}
private static void tick() {
Thread timer = new Thread(new TimerTask());
timer.setName("sentinel-timer-task");
timer.start();
}
static class TimerTask implements Runnable {
@Override
public void run() {
System.out.println("begin to statistic!!!");
long oldTotal = 0;
long oldPass = 0;
long oldBlock = 0;
while (!stop) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
long globalTotal = total.get();
long oneSecondTotal = globalTotal - oldTotal;
oldTotal = globalTotal;
long globalPass = pass.get();
long oneSecondPass = globalPass - oldPass;
oldPass = globalPass;
long globalBlock = block.get();
long oneSecondBlock = globalBlock - oldBlock;
oldBlock = globalBlock;
System.out.println(seconds + ", " + TimeUtil.currentTimeMillis() + ", total:"
+ oneSecondTotal + ", pass:"
+ oneSecondPass + ", block:" + oneSecondBlock);
if (seconds-- <= 0) {
stop = true;
}
}
System.exit(0);
}
}
}
复制代码
1.4 ソースコード分析
1.4.1 適応電流制限インレット SystemSlot
@Spi(order = Constants.ORDER_SYSTEM_SLOT)
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
// todo entry方法中调用SystemRuleManager.checkSystem方法,这里是自适应限流的关键点
SystemRuleManager.checkSystem(resourceWrapper, count);
// 在职责链上继续调用下一个slot节点。
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
}
复制代码
1.4.2 システムルールマネージャー
1.4.2.1 クラス図
sentinel自适应限流通过SystemRuleManager类来实现,它里面封装了BBR算法的实现,以及系统指标的采集,接下来我们看下它的类图以及核心属性。
我们来看下SystemRuleManager
的一些属性:
//系统最大负载
private static volatile double highestSystemLoad = Double.MAX_VALUE;
/**
* cpu usage, between [0, 1]
*/
//CPU使用率,介于[0,1]之间
private static volatile double highestCpuUsage = 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;
//采集系统cpu load、cpu使用率的实现。
private static SystemStatusListener statusListener = null;
复制代码
1.4.2.2 核心方法源码分析
-
checkSystem
:public static void checkSystem(ResourceWrapper resourceWrapper, int count) throws BlockException { // 检查资源是否为空,如果为空直接返回 if (resourceWrapper == null) { return; } // Ensure the checking switch is on. // 判断系统自适应限流是否开启,未开启直接返回。 if (!checkSystemStatus.get()) { return; } // for inbound traffic only // 判断资源的流量是否为入口流量,Sentinel系统自适应限流只对入口流量生效 if (resourceWrapper.getEntryType() != EntryType.IN) { return; } // total qps // 从Constants.ENTRY_NODE获取当前qps,如果当前qps大于SystemRule配置的阈值,直接抛SystemBlockException异常 double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.passQps(); if (currentQps + count > qps) { throw new SystemBlockException(resourceWrapper.getName(), "qps"); } // total thread // 从Constants.ENTRY_NODE获取当前thread,如果当前thread大于SystemRule配置的阈值,直接抛SystemBlockException 异常 int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum(); if (currentThread > maxThread) { throw new SystemBlockException(resourceWrapper.getName(), "thread"); } // 从Constants.ENTRY_NODE获取当前avgRT,如果当前avgRT大于SystemRule配置的阈值,直接抛SystemBlockException异常 double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt(); if (rt > maxRt) { throw new SystemBlockException(resourceWrapper.getName(), "rt"); } // load. BBR algorithm. // 进行bbr算法校验 // 校验系统负载开关是否打开,当前系统load是否大于配置的系统load,如果都满足则继续校验 if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) { // 调用checkBbr方法,之前我们有说过系统通过流量:T ≈ QPS * Avg(RT)时 // 我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,所以checkBbr方法计算的公式以秒为单位:T=QPS*RT/1000。 // 如果当前线程数大于T,则进行拦截 if (!checkBbr(currentThread)) { throw new SystemBlockException(resourceWrapper.getName(), "load"); } } // cpu usage // 判断当前CPU使用率是否大于SystemRule配置的阈值,如果是则抛出SystemBlockException异常 if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) { throw new SystemBlockException(resourceWrapper.getName(), "cpu"); } } //bbr算法 private static boolean checkBbr(int currentThread) { if (currentThread > 1 && currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) { return false; } return true; } 复制代码
-
Constants.ENTRY_NODE
- 自适应限流使用的是全局的ClusterNode节点,这就是说自适应限流的维度是整个系统。
public final static ClusterNode ENTRY_NODE = new ClusterNode(TOTAL_IN_RESOURCE_NAME, ResourceTypeConstants.COMMON); 复制代码
-
系统指标采集运行
@SuppressWarnings("PMD.ThreadPoolCreationRule") private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-system-status-record-task", true)); static { checkSystemStatus.set(false); statusListener = new SystemStatusListener(); scheduler.scheduleAtFixedRate(statusListener, 0, 1, TimeUnit.SECONDS); currentProperty.addListener(listener); } 复制代码
SystemRuleManager
类中定义了ScheduledExecutorService
线程池,在静态块里面触发SystemStatusListener
类的运行,运行时间是1秒钟一次,这表示Sentinel
的自适应保护信息采集为1秒钟采集系统load、cpu
信息。
1.4.3 系统指标采集源码分析
SystemStatusListener
类图:
该类实现了runnable
接口,他通过一个线程每隔一秒执行一次load、cpu usage
信息的采集。
- 源码分析
public class SystemStatusListener implements Runnable {
volatile double currentLoad = -1;
volatile double currentCpuUsage = -1;
volatile String reason = StringUtil.EMPTY;
volatile long processCpuTime = 0;
volatile long processUpTime = 0;
public double getSystemAverageLoad() {
return currentLoad;
}
public double getCpuUsage() {
return currentCpuUsage;
}
@Override
public void run() {
try {
// Sentinel通过jdk:ManagementFactory类获取系统load、cpu等信息。
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
// 获取当前系统load。
currentLoad = osBean.getSystemLoadAverage();
/*
* Java Doc copied from {@link OperatingSystemMXBean#getSystemCpuLoad()}:</br>
* Returns the "recent cpu usage" for the whole system. This value is a double in the [0.0,1.0] interval.
* A value of 0.0 means that all CPUs were idle during the recent period of time observed, while a value
* of 1.0 means that all CPUs were actively running 100% of the time during the recent period being
* observed. All values between 0.0 and 1.0 are possible depending of the activities going on in the
* system. If the system recent cpu usage is not available, the method returns a negative value.
*/
// 获取当前系统cpu usage
double systemCpuUsage = osBean.getSystemCpuLoad();
// calculate process cpu usage to support application running in container environment
RuntimeMXBean runtimeBean = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class);
// 获取系统cpu运行时间
long newProcessCpuTime = osBean.getProcessCpuTime();
// 获取当前jvm cpu运行时间
long newProcessUpTime = runtimeBean.getUptime();
// 获取系统cpu核心数
int cpuCores = osBean.getAvailableProcessors();
long processCpuTimeDiffInMs = TimeUnit.NANOSECONDS
.toMillis(newProcessCpuTime - processCpuTime);
long processUpTimeDiffInMs = newProcessUpTime - processUpTime;
// 计算CPU使用率
double processCpuUsage = (double) processCpuTimeDiffInMs / processUpTimeDiffInMs / cpuCores;
processCpuTime = newProcessCpuTime;
processUpTime = newProcessUpTime;
currentCpuUsage = Math.max(processCpuUsage, systemCpuUsage);
if (currentLoad > SystemRuleManager.getSystemLoadThreshold()) {
writeSystemStatusLog();
}
} catch (Throwable e) {
RecordLog.warn("[SystemStatusListener] Failed to get system metrics from JMX", e);
}
}
private void writeSystemStatusLog() {
StringBuilder sb = new StringBuilder();
sb.append("Load exceeds the threshold: ");
sb.append("load:").append(String.format("%.4f", currentLoad)).append("; ");
sb.append("cpuUsage:").append(String.format("%.4f", currentCpuUsage)).append("; ");
sb.append("qps:").append(String.format("%.4f", Constants.ENTRY_NODE.passQps())).append("; ");
sb.append("rt:").append(String.format("%.4f", Constants.ENTRY_NODE.avgRt())).append("; ");
sb.append("thread:").append(Constants.ENTRY_NODE.curThreadNum()).append("; ");
sb.append("success:").append(String.format("%.4f", Constants.ENTRY_NODE.successQps())).append("; ");
sb.append("minRt:").append(String.format("%.2f", Constants.ENTRY_NODE.minRt())).append("; ");
sb.append("maxSuccess:").append(String.format("%.2f", Constants.ENTRY_NODE.maxSuccessQps())).append("; ");
RecordLog.info(sb.toString());
}
}
复制代码
1.5 Sentinel后台配置
配置系统保护规则:
那么,后台配置是怎么映射进入的呢?
在 SystemRuleManager
中,有一个静态方法(loadRules(List<SystemRule> rules)
)去初始化SystemRule配置。
public static void loadRules(List<SystemRule> rules) {
currentProperty.updateValue(rules);
}
复制代码
而在更新currentProperty
的时候,实质是通知观察者去更新,这里使用的是观察者模式
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
//观察者
protected Set<PropertyListener<T>> listeners = Collections.synchronizedSet(new HashSet<PropertyListener<T>>());
//更新值
@Override
public boolean updateValue(T newValue) {
//如果两个值一样,则返回false,不修改
if (isEqual(value, newValue)) {
return false;
}
value = newValue;
//通知各个观察者
for (PropertyListener<T> listener : listeners) {
listener.configUpdate(newValue);
}
return true;
}
//判断两个对象是否一致
private boolean isEqual(T oldValue, T newValue) {
if (oldValue == null && newValue == null) {
return true;
}
if (oldValue == null) {
return false;
}
return oldValue.equals(newValue);
}
public void close() {
listeners.clear();
}
}
复制代码
这里的观察者为**SystemPropertyListener
**,在SystemRuleManager
的静态方法区已经添加进去。
public class SystemRuleManager {
//观察者,当systemRule配置发生变更时,会通知该Listener
private final static SystemPropertyListener listener = new SystemPropertyListener();
static {
checkSystemStatus.set(false);
statusListener = new SystemStatusListener();
scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
//添加观察者
currentProperty.addListener(listener);
}
}
复制代码
而在SystemPropertyListener
更新的时候,会先关闭系统检查,当配置修改完成之后,再启用。
static class SystemPropertyListener extends SimplePropertyListener<List<SystemRule>> {
@Override
public void configUpdate(List<SystemRule> rules) {
//恢复到默认状态
restoreSetting();
if (rules != null && rules.size() >= 1) {
for (SystemRule rule : rules) {
//加载配置
loadSystemConf(rule);
}
} else {
checkSystemStatus.set(false);
}
}
//重置配置信息
protected void restoreSetting() {
checkSystemStatus.set(false);
// should restore changes
highestSystemLoad = Double.MAX_VALUE;
highestCpuUsage = Double.MAX_VALUE;
maxRt = Long.MAX_VALUE;
maxThread = Long.MAX_VALUE;
qps = Double.MAX_VALUE;
highestSystemLoadIsSet = false;
maxRtIsSet = false;
maxThreadIsSet = false;
qpsIsSet = false;
}
}
复制代码
修改则判断是否小于默认配置,由于之前已经重新初始化过了,所以如果有修改,肯定会比默认的值小。
public static void loadSystemConf(SystemRule rule) {
boolean checkStatus = false;
if (rule.getHighestSystemLoad() >= 0) {
highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());
highestSystemLoadIsSet = true;
checkStatus = true;
}
if (rule.getHighestCpuUsage() >= 0) {
highestCpuUsage = Math.min(highestCpuUsage, rule.getHighestCpuUsage());
highestCpuUsageIsSet = 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);
}
复制代码
配置加载完毕,如果有SystemRule
配置,则将checkSystemStatus
改为true
。
1.6 总结
经过以上源码分析,我们可以得出几点结论:
- Sentinel自适应限流原理采用了BBR算法
- Sentinel自适应限流维度是整个系统,当系统负载过大时会触发限流。
- Sentinel自适应限流信息采集指标为1秒钟一次。
- Sentinel によって収集されるインジケーターはオペレーティング システム レベルであるため、適応電流制限を使用する場合は、アプリケーション サーバーが高負荷の他のサービスをデプロイしないことをお勧めします。
2.オーソリティスロット
AuthorizationSlot
ブラック・アンド・ホワイト・リストに従って、ブラック・アンド・ホワイト・リストの制御が行われ、リソースが構成AuthorityRule
されている場合、リソース要求の要求元 (origin) が構成規則LimitApp
( (,)隔开
) にあるかどうかと、ポリシーに従って判断されます。ポリシーの判断、チェックに合格したかどうか。
-
ホワイトリストに登録されている場合
- 原点が にあるかどうかを判断し
limitApp
、そうであれば true を返し、そうでなければ false を返します
- 原点が にあるかどうかを判断し
-
ブラックリストに載っている場合
- 原点が にあるかどうかを判断し
limitApp
、そうであれば false を返し、そうでなければ true を返します
- 原点が にあるかどうかを判断し
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
throws Throwable {
//检查黑白名单
checkBlackWhiteAuthority(resourceWrapper, context);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
//获取认证的规则
Map<String, List<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
if (authorityRules == null) {
return;
}
//根据resourceName获取该资源下对应的规则
List<AuthorityRule> rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}
for (AuthorityRule rule : rules) {
//认证检查
if (!AuthorityRuleChecker.passCheck(rule, context)) {
throw new AuthorityException(context.getOrigin(), rule);
}
}
}
}
复制代码
チェックロジックは次のAuthorityRuleChecker
とおりです。
final class AuthorityRuleChecker {
static boolean passCheck(AuthorityRule rule, Context context) {
String requester = context.getOrigin();
// 获取orgin请求来源,如果为请求来源为null或者limitApp为null则直接返回通过
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
return true;
}
//判断limitApp是否含有origin
int pos = rule.getLimitApp().indexOf(requester);
boolean contain = pos > -1;
if (contain) {
boolean exactlyMatch = false;
String[] appArray = rule.getLimitApp().split(",");
for (String app : appArray) {
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}
contain = exactlyMatch;
}
//根据策略处理是否包含,判断是否通过
int strategy = rule.getStrategy();
if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
return false;
}
if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
return false;
}
return true;
}
private AuthorityRuleChecker() {}
}
复制代码
AuthorityRule
構成の更新は、SystemSlot
更新に依存する方法と同じです。AuthorityRuleManager
loadRules
2.1 Sentinel バックグラウンド設定
Sentinel バージョン 1.8 のソース コードでは、Context の作成時に orion がデフォルトで空の文字列であるため、権限制御を確認する場合は、これを特定の orion ソースに変更する必要があります。コードは次のとおりですcom.alibaba.csp.sentinel.CtSph.InternalContextUtil#internalEnter(java.lang.String)
。
参考記事
Sentinel1.8.5 ソースコード github アドレス (注)
Sentinel ソースコード解析
Sentinel 公式 Web サイト
Setinel ソースコード読み取り:
シンプルな言語によるシステム適応保護の原理 AuthoritySlot