`
cywhoyi
  • 浏览: 421997 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

电路熔断器(Circuit Breaker)自我思考

    博客分类:
  • JAVA
阅读更多
作者:美团点评技术团队
链接:https://zhuanlan.zhihu.com/p/23711137
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在我们的工程实践中,偶尔会遇到一些服务由于网络连接超时,系统有异常或load过高出现暂时不可用等情况,导致对这些服务的调用失败,可能需要一段时间才能修复,这种对请求的阻塞可能会占用宝贵的系统资源,如:内存,线程,数据库连接等等,最坏的情况下会导致这些资源被消耗殆尽,使得系统里不相关的部分所使用的资源也耗尽从而拖累整个系统。在这种情况下,调用操作能够立即返回错误而不是等待超时的发生或者重试可能是一种更好的选择,只有当被调用的服务有可能成功时我们再去尝试。

熔断器模式可以防止我们的系统不断地尝试执行可能会失败的调用,使得我们的系统继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器模式也可以使我们系统能够检测错误是否已经修正,如果已经修正,系统会再次尝试调用操作。下图是个使用熔断器模式的调用流程:

可以从图中看出,当超时出现的次数达到一定条件后,熔断器会触发打开状态,客户端的下次调用将直接返回,不用等待超时产生。

在熔断器内部,往往有以下几种状态:

1)闭合(closed)状态:该状态下能够对目标服务或方法进行正常的调用。熔断器类维护了一个时间窗口内调用失败的次数,如果某次调用失败,则失败次数加1。如果最近失败次数超过了在给定的时间窗口内允许失败的阈值(可以是数量也可以是比例),则熔断器类切换到断开(Open)状态。此时熔断器设置了一个计时器,当时钟超过了该时间,则切换到半断开(Half-Open)状态,该睡眠时间的设定是给了系统一次机会来修正导致调用失败的错误。

2)断开(Open)状态:在该状态下,对目标服务或方法的请求会立即返回错误响应,如果设置了fallback方法,则会进入fallback的流程。

3)半断开(Half-Open)状态:允许对目标服务或方法的一定数量的请求可以去调用服务。如果这些请求对服务的调用成功,那么可以认为之前导致调用失败的错误已经修正,此时熔断器切换到闭合状态(并且将错误计数器重置);如果这一定数量的请求有调用失败的情况,则认为导致之前调用失败的问题仍然存在,熔断器切回到断开方式,然后开始重置计时器来给系统一定的时间来修正错误。半断开状态能够有效防止正在恢复中的服务被突然而来的大量请求再次拖垮。

 

  • 方案一:基于spring AOP的切面设计

 

@Component
@Aspect
public class CircuitBreakerAdvice implements BeanFactoryAware {

    private static final Logger LOG = LoggerFactory.getLogger(CircuitBreakerAdvice.class);

    @Autowired
    private BeanFactory beanFactory;

    public CircuitBreakerAdvice() {
        LOG.debug("CircuitBreaker created");
    }

    @Around("@annotation(ch.tom.cb.CircuitBreaker)")
    public Object intercept(ProceedingJoinPoint point) throws Throwable {
        CircuitBreakerConfig circuitBreakerConfig = getCircuitBreaker(point);
        try {
            return callTargetMethod(point, circuitBreakerConfig);
        } catch (Throwable e) {
            if (!circuitBreakerConfig.isExceptionExcluded(e)) {
                LOG.debug("closing circuit due to received exception");
                circuitBreakerConfig.getCircuitBreakerController().setState(CircuitBreakerState.CLOSED);
            }
            throw e;
        }
    }

    private Object callTargetMethod(ProceedingJoinPoint point, CircuitBreakerConfig circuitBreakerConfig) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        CircuitBreakerController controller = circuitBreakerConfig.getCircuitBreakerController();


        if (isMyCallTheOneThatReopensTheCircuit(circuitBreakerConfig) || circuitBreakerConfig.getCircuitBreakerController().isStateOpen()) {
            LOG.debug("interception method {} and state is open proceeding.", method.getName());
            if (circuitBreakerConfig.getServiceTimeout() == null){
                Object returnValue = point.proceed();
                if (circuitBreakerConfig.getCircuitBreakerController().isStateHalfOpen()){
                    circuitBreakerConfig.getCircuitBreakerController().setState(CircuitBreakerState.OPEN);
                }
                return returnValue;
            }else {
                FutureTask circuitBreakerTimeoutFuture = new FutureTask(new CircuitBreakerTimeoutFuture(point));
                Object returnValue = null;
                try {
                    controller.getTimeoutExecutor().execute(circuitBreakerTimeoutFuture);
                    returnValue = circuitBreakerTimeoutFuture.get(circuitBreakerConfig.getServiceTimeout().toMillis(), TimeUnit.MILLISECONDS);
                    if (circuitBreakerConfig.getCircuitBreakerController().isStateHalfOpen()){
                        circuitBreakerConfig.getCircuitBreakerController().setState(CircuitBreakerState.OPEN);
                    }
                    return returnValue;
                }catch (TimeoutException toe){
                    circuitBreakerTimeoutFuture.cancel(true);
                    LOG.debug("timeout occured on method {} closing circuit.", method.getName());
                    circuitBreakerConfig.getCircuitBreakerController().setState(CircuitBreakerState.CLOSED);
                    return returnFromFallBackService(point,circuitBreakerConfig);
                }
            }

        } else {
            return returnFromFallBackService(point, circuitBreakerConfig);
        }

    }

    private boolean isMyCallTheOneThatReopensTheCircuit(CircuitBreakerConfig config){
        if (config.getCircuitBreakerController().isForced()){
            return false;
        }
        synchronized (config){
                if (config.getCircuitBreakerController().isStateClosed() &&
                        config.getCircuitBreakerController().isMinClosedTimeReached(config.getMinClosedDuration())){
                    //ok try this call.
                    config.getCircuitBreakerController().setState(CircuitBreakerState.HALFOPEN);
                    LOG.debug("try to reopen circuit with one call");
                    return true;
                }
            }
        return false;
    }

    private Object returnFromFallBackService(ProceedingJoinPoint point, CircuitBreakerConfig config) throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Object closedProvider = config.getClosedServiceProvider();
        if (closedProvider == null){
            LOG.debug("interception method {} and state is open proceeding.", method.getName());
            throw new CircuitBreakerException();
        }else {
            LOG.debug("interception method {} and state is closed call fallback.", method.getName());
            Method closedProviderMethod = closedProvider.getClass().getMethod(method.getName(), method.getParameterTypes());
            return closedProviderMethod.invoke(closedProvider, point.getArgs());
        }
    }

    private CircuitBreakerConfig getCircuitBreaker(ProceedingJoinPoint point) throws NoSuchMethodException {
        CircuitBreaker circuitBreaker = null;
        MethodSignature signature = (MethodSignature) point.getSignature();
        //method can be an interface.
        Method method = signature.getMethod();
        Method concreteMethod = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
        circuitBreaker = concreteMethod.getAnnotation(CircuitBreaker.class);
        CircuitBreakerConfig config = (CircuitBreakerConfig) beanFactory.getBean(circuitBreaker.config());
        if (config == null) {
            throw new IllegalStateException(String.format("CircuitBreakerConfig with name %s could not be found", circuitBreaker.config()));
        }
        return config;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }


}

 

 

 

 

 

 

1
0
分享到:
评论

相关推荐

    详解spring cloud分布式关于熔断器

    熔断器相当于电路中的保险丝、保护器,它可以实现快速失败。如果熔断器在某一段时间里侦测到许多类似的错误,它将不再访问远程服务器,会强迫以后的访问都会快速失败,从而防止某个服务不断地尝试执行可能会失败的...

    关于FC回路是否启用速断保护的探讨

    FC回路指的是以馈线断路器(Feeder Circuit Breaker,简称FC)作为控制保护元件的电路。 2. 厂用6kV系统照明变送电熔断器保护动作案例分析:在电力系统的实际运用中,熔断器保护动作可能是由多种因素引起的。文章...

    spring-cloud-netflix-hystrix原理.rar

    1. **熔断器(Circuit Breaker)**:Hystrix 的核心机制,它模拟了电路板的开关状态,包括闭合、打开和半开三种状态。在正常情况下,熔断器处于闭合状态,允许请求通过。当服务连续出现大量失败时,熔断器会打开,...

    电气控制基础元件认识

    当电路发生短路或过载时,流经熔断器的电流会迅速增大,导致熔断器内部的金属丝(或片)熔化并断开电路,从而起到保护作用。 **2. 应用方式** 熔断器通常串联在被保护电路的首端,以便于一旦发生故障能够立即切断...

    全国通用备战2020中考物理专题1.19生活用电含解析20200219548

    - 熔断器(Fuses)的作用是保护电路,当电流过大时自动熔断,避免电路过热引发火灾。如果熔断器熔丝熔断,不应使用铜丝或其他金属代替,而应更换相同规格的熔丝。 - 使用三线插座时,地线必须完好,确保电器的金属...

    ABB 终端配电保护产品.pdf

    5. 熔断器(Fuse):熔断器是最基本的过电流保护元件,它包含一个熔丝,当电流超过熔丝额定值时,熔丝会熔断,从而切断电路。ABB的熔断器产品具有快速熔断和限流的特性,能有效防止电气设备因过载或短路而损坏。 6....

    低压电器图形符号及文字符号大全[参照].pdf

    - **熔断器(Fuse, FU)**:电流过大时熔断,断开电路。 5. **其他元件**: - **发电机(Generator, G)**:产生电能。 - **电动机(Motor, M)**:电动设备的动力源,包括异步电机(MA)、同步电机(MS)、直流...

    电子功用-断路器、配电箱

    断路器的主要类型有空气开关、熔断器、电磁式断路器、微电子型断路器等。其中,空气开关(又称MCB,Miniature Circuit Breaker)是最常见的,适用于住宅和小型商业场所,能够通过感应电流变化来断开电路;熔断器则是...

    PROTUES元件库

    * FUSE 熔断器 * CIRCUIT BREAKER 熔断丝 * COAX 同轴电缆 * CON 插口 * CRYSTAL 晶体整荡器 * DB 并行插口 此外,PROTUES元件库还收录了许多其他电子元件,包括模拟数字式集成块、触发器、比较放大器、INTEL...

    proteus元器件名称

    * FUSE 熔断器:熔断器是电子电路中的保护元器件,用于防止电路过载。 * INDUCTOR 电感:电感是电子电路中的基本元器件,用于储存磁场能量。 * LAMP 灯泡:灯泡是电子电路中的基本元器件,用于发出光信号。 * ...

    家庭电路与家庭用电PPT课件.pptx

    - 熔断器应安装在闸刀开关之后,一旦熔丝熔断,可以及时断开电路。 - 支路开关通常接在火线上,以确保断开开关后,电器不再带电。 - 两眼插座的右眼接火线,左眼接零线;三眼插座的左眼接火线,中间眼接地线,...

    Protues仿真软件器件名称

    57. FUSE 熔断器:熔断器是一种保护电路的器件,用于防止电路过载。 58. INDUCTOR 电感:电感是一种储存磁场的器件,用于滤波、耦合等应用。 59. INDUCTOR IRON 带铁芯电感:带铁芯电感是一种电感器件,用于滤波、...

    电路仿真常用元器件.docx

    9. **熔断丝 (CIRCUIT BREAKER)**:熔断丝在电流过载时断开电路,提供保护功能。 10. **晶体振荡器 (CRYSTAL)**:晶体振荡器产生稳定的频率信号,常用于时钟信号的生成。 11. **继电器 (PELAY-DPDT)**:双刀双掷继...

    抓斗式卸船机

    - **熔断器负荷开关(Fused Disconnect Switch)**:结合了熔断器和开关的功能,既起到过载保护作用,又方便电路的通断操作。 - **断路器(Circuit Breaker)**:广泛应用于电力系统中,能够自动切断故障电路。 - **...

    天正 TGM65系列高分断小型断路器产品样本.pdf

    小型断路器通常安装在配电盘中,配合其他配电设备使用,比如熔断器、漏电保护器等。 2. 高分断能力的意义 高分断能力是指断路器在发生故障电流时,能够可靠分断的能力。高分断能力保证了即便是在短路电流很大的情况...

    最全Proteus元件库元件名称及中英对照-51单片机仿真软件Proreus学习必用.doc

    * CIRCUIT BREAKER 熔断丝:熔断器,用于保护电路 * COAX 同轴电缆:同轴电缆,用于传输信号 * CON 插口:插口,用于连接电路 * CRYSTAL 晶体整荡器:晶体整荡器,用于生成稳定的时钟信号 三、逻辑门 * DIODE ...

    漏电保护模块(RCCB)与漏电保护断路器(RCBO)的区别.zip

    RCCB通常不包含过载或短路保护功能,需要配合熔断器或断路器一起使用,以实现完整的电路保护。 相反,RCBO则是一个集成了过电流保护和漏电保护的综合装置。它在RCCB的基础上增加了过载和短路保护功能,一旦检测到...

    proteus 元件库中英文对照表

    * FUSE - 熔断器 * INDUCTOR - 电感 * INDUCTOR IRON - 带铁芯电感 * INDUCTOR3 - 可调电感 6. 显示器: * JFET N - N 沟道场效应管 * JFET P - P 沟道场效应管 * LAMP - 灯泡 * LAMP NEDN - 起辉器 * LED ...

    单片机的proteus仿真元器件对照表

    * CIRCUIT BREAKER 熔断丝:用于保护电路的熔断丝。 4. Microcontroller 元器件 * MICROPROCESSOR 微处理器:执行指令并控制其他元器件的微处理器。 * MICROCONTROLLER 单片机:执行指令并控制其他元器件的单片机...

    ABB S500微型断路器技术资料.pdf

    微型断路器是取代传统的熔断器的现代电气保护设备,因为它们可以在故障清除后重新使用,而不需要更换熔断丝。 ABB S500微型断路器属于ABB的低压配电产品线。ABB是全球知名的电气工程公司,业务遍及发电、输电、配电...

Global site tag (gtag.js) - Google Analytics