直接从Sentinel 源码demo ExceptionRatioCircuitBreakerDemo看起

直接看他的main函数

public static void main(String[] args) throws Exception {
initDegradeRule();
...... final int concurrency = 8;
for (int i = 0; i < concurrency; i++) {
Thread entryThread = new Thread(() -> {
while (true) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
sleep(ThreadLocalRandom.current().nextInt(5, 10));
pass.addAndGet(1); // Error probability is 45%
if (ThreadLocalRandom.current().nextInt(0, 100) > 55) {
// biz code raise an exception.
throw new RuntimeException("oops");
}
} catch (BlockException e) {
block.addAndGet(1);
sleep(ThreadLocalRandom.current().nextInt(5, 10));
} catch (Throwable t) {
bizException.incrementAndGet();
// It's required to record exception here manually.
Tracer.traceEntry(t, entry);
} finally {
total.addAndGet(1);
if (entry != null) {
entry.exit();
}
}
}
});
entryThread.setName("sentinel-simulate-traffic-task-" + i);
entryThread.start();
}
}

从源码中可以看出,比如限流那一套多了一个在catch Throwable的操作 Tracer.traceEntry(t, entry);

看一下其中的源码

    public static void traceEntry(Throwable e, Entry entry) {
if (!shouldTrace(e)) {
return;
}
traceEntryInternal(e, entry);
} private static void traceEntryInternal(/*@NeedToTrace*/ Throwable e, Entry entry) {
if (entry == null) {
return;
} entry.setError(e);
} // Entry.java
public void setError(Throwable error) {
this.error = error;
}

从源码可以看出 这里仅仅是将error赋给entry的成员error。这里是为了后面的finally逻辑做铺垫

异常被触发并执行完catch逻辑后,会执行finally的逻辑,会调用entry.exit();方法。
//CtEntry.java

    protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
if (context != null) {
// Null context should exit without clean-up.
if (context instanceof NullContext) {
return;
} if (context.getCurEntry() != this) {
String curEntryNameInContext = context.getCurEntry() == null ? null
: context.getCurEntry().getResourceWrapper().getName();
// Clean previous call stack.
CtEntry e = (CtEntry) context.getCurEntry();
while (e != null) {
e.exit(count, args);
e = (CtEntry) e.parent;
}
String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
+ ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
resourceWrapper.getName());
throw new ErrorEntryFreeException(errorMessage);
} else {
// Go through the onExit hook of all slots.
if (chain != null) {
//最终会调用到chain的exit
chain.exit(context, resourceWrapper, count, args);
}
// Go through the existing terminate handlers (associated to this invocation).
callExitHandlersAndCleanUp(context); // Restore the call stack.
context.setCurEntry(parent);
if (parent != null) {
((CtEntry) parent).child = null;
}
if (parent == null) {
// Default context (auto entered) will be exited automatically.
if (ContextUtil.isDefaultContext(context)) {
ContextUtil.exit();
}
}
// Clean the reference of context in current entry to avoid duplicate exit.
clearEntryContext();
}
}
}
最终会执行exitForContext,并调用到chain.exit(context, resourceWrapper, count, args);, 于是会调用到DegradeSlot的 exit
    @Override
public void exit(Context context, ResourceWrapper r, int count, Object... args) {
Entry curEntry = context.getCurEntry();
if (curEntry.getBlockError() != null) {
fireExit(context, r, count, args);
return;
}
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
fireExit(context, r, count, args);
return;
} if (curEntry.getBlockError() == null) {
// passed request
for (CircuitBreaker circuitBreaker : circuitBreakers) {
//这里是最关键的一步
circuitBreaker.onRequestComplete(context);
}
} fireExit(context, r, count, args);
}
这里的 circleBreaker 是 ExceptionCircuitBreaker 对象,最终会调用
    @Override
public void onRequestComplete(Context context) {
Entry entry = context.getCurEntry();
if (entry == null) {
return;
}
// 这里和上面的entry缓存error就关联上了,发现错误并计数
Throwable error = entry.getError();
SimpleErrorCounter counter = stat.currentWindow().value();
if (error != null) {
counter.getErrorCount().add(1);
}
counter.getTotalCount().add(1);
//核心函数,根据统计,继续状态切换 close -> open (--> open -> half-open)
handleStateChangeWhenThresholdExceeded(error);
}
 private void handleStateChangeWhenThresholdExceeded(Throwable error) {

       ......
List<SimpleErrorCounter> counters = stat.values();
long errCount = 0;
long totalCount = 0;
for (SimpleErrorCounter counter : counters) {
errCount += counter.errorCount.sum();
totalCount += counter.totalCount.sum();
}
if (totalCount < minRequestAmount) {
return;
}
double curCount = errCount;
if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {
// Use errorRatio
curCount = errCount * 1.0d / totalCount;
}
if (curCount > threshold) {
transformToOpen(curCount);
}
}
通过counter计算出单位统计时间的错误量,判断是否超过锚点,调用transformToOpen -> fromCloseToOpen
    protected boolean fromCloseToOpen(double snapshotValue) {
State prev = State.CLOSED;
if (currentState.compareAndSet(prev, State.OPEN)) {
updateNextRetryTimestamp(); notifyObservers(prev, State.OPEN, snapshotValue);
return true;
}
return false;
}

保证线程安全,通过CAS (compareAndSet) 将状态改为OPEN。

这是本资源对应的熔断器状态变为OPEN,之后流量进来时

//DegradeSlot.java

    void performChecking(Context context, ResourceWrapper r) throws BlockException {
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
return;
}
for (CircuitBreaker cb : circuitBreakers) {
//调用 breaker的tryPass方法
if (!cb.tryPass(context)) {
throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
}
}
}
//AbstractCircuitBreaker.java
@Override
public boolean tryPass(Context context) {
// Template implementation.
if (currentState.get() == State.CLOSED) {
return true;
}
if (currentState.get() == State.OPEN) {
// For half-open state we allow a request for probing.
//状态为Open,会进来这里
return retryTimeoutArrived() && fromOpenToHalfOpen(context);
}
return false;
}

会先检查,断开时间是否已到,如果到了,会切换到半开状态,然后返回false,不通过并抛出DegradeException

之后的第一个请求进来如果没有抛出异常,则将状态从halfOpen 改为Close,进行放行状态,这个过程在 entry.exit 时调用handleStateChangeWhenThresholdExceeded时完成

Sentinel 源码分析- 熔断降级原理分析的更多相关文章

  1. 老李推荐:第6章8节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-小结

    老李推荐:第6章8节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-小结   本章我们重点围绕处理网络过来的命令的MonkeySourceNetwork这个事 ...

  2. 老李推荐:第6章7节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-注入按键事件实例

    老李推荐:第6章7节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-注入按键事件实例   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜 ...

  3. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列   事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些 ...

  4. 老李推荐:第6章4节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-翻译命令字串

    老李推荐:第6章4节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-翻译命令字串   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自 ...

  5. 老李推荐:第6章5节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-事件

    老李推荐:第6章5节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-事件   从网络过来的命令字串需要解析翻译出来,有些命令会在翻译好后直接执行然后返回,但有 ...

  6. 老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

    老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类   每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行 ...

  7. 老李推荐:第6章2节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-获取命令字串

    老李推荐:第6章2节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-获取命令字串   从上一节的描述可以知道,MonkeyRunner发送给Monkey的命令 ...

  8. 老李推荐:第5章7节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles

    老李推荐:第5章7节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles   poptest是国内唯一一家培养测试开 ...

  9. 老李推荐:第5章6节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 初始化事件源

    老李推荐:第5章6节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 初始化事件源   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试 ...

随机推荐

  1. SAP PP- OPK8生产订单打印 配置Smart form.

    OPK8 正常情况下是不可以配置Smart form 的 OPK8进入工单打印配置界面,选择Forms, 你会发现只有Script form 和PDF form(Adobe form)可选的,没有配置 ...

  2. kali渗透测试阅读目录

    一.渗透测试介绍 渗透测试介绍及渗透环境配置 二.信息收集 kali 信息收集 三.漏洞扫描 kali 漏洞扫描 四.漏洞利用 kali msf漏洞利用

  3. NC200211 装备合成

    NC200211 装备合成 题目 题目描述 牛牛有 \({x}\) 件材料 \({a}\) 和 \({y}\) 件材料 \({b}\) ,用 \({2}\) 件材料 \({a}\) 和 \({3}\) ...

  4. Systemverilog-- OOP--对象的拷贝

    ​ 目录 浅拷贝: 定义拷贝函数: 拷贝函数总结: 浅拷贝: Packet p1; Packet p2; p1 = new; p2 = new p1; 在创建p2对象时,将从p1拷贝其成员变量例如 i ...

  5. C#请求HTTPS地址的故障分析和TLS知识点总结

    背景介绍 近期收到同事反馈,在C#程序中通过HTTPClient请求一个HTTPS的地址时,在本地开发环境和测试环境均能正常执行,而部署到生产环境后发生异常且稳定复现,异常提示为:[请求被中止: 未能 ...

  6. git的基本操作命令和码云的注册使用

    Git文件操作文件的四种状态版本控制就是对文件的版本控制,要对文件进行修改.提交等操作,首先要知道文件当前在什么状态,不然可能会提交了现在还不想提交的文件,或者要提交的文件没提交上. Untracke ...

  7. protobuf 的交叉编译使用(C++)

    前言 为了提高通信效率,可以采用 protobuf 替代 XML 和 Json 数据交互格式,protobuf 相对来说数据量小,在进程间通信或者设备之间通信能够提高通信速率.下面介绍 protobu ...

  8. 4G巴歇尔槽流量采集网关

    首先向大家展示下拓扑图: 金鸽科技R10物联网网关,带有一个RS485口可以采集巴歇尔槽的液位状态,还提供一个网口用于给摄像头和现场其他的网络设备提供网络传输通道!R10A内置了巴歇尔槽液位换算成流量 ...

  9. 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(9)-Fiddler如何设置捕获Https会话

    1.简介 由于近几年来各大网站越来越注重安全性都改成了https协议,不像前十几年前直接是http协议直接裸奔在互联网.还有的小伙伴或者童鞋们按照上一篇宏哥的配置都配置好了,想大展身手抓一下百度的包, ...

  10. Mpvue1.0+Python3.7+Django2.0.4实现微信小程序的支付功能

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_112 其实微信支付有很多种形式,刷脸,扫码,APP支付,小程序支付等,这边只说明小程序支付的实现,不过原理上都大同小异. 首先,需 ...