分布式中间件──断路器

一、断路器介绍

断路器,在我们中学时的物理课中讲过,为了保护电路,防止短路、过载一般会有保险丝,现在有更高级的保护措施和设备,但目的始终保持一致。在我们的程序中也有类似的情况,用户请求就好比电路中的负载,虽然现在很多系统都采用分布式,但单机所能承受的负载是有限的,同时也不能完全保证程序100%不出现问题,所以程序中的“保险丝”就能有效的发挥作用了。

二、代码实现

话不多说,直接上代码实现

  • CircuitBreaker ····· 断路器
  • CircuitBreakerConfig ····· 断路器配置
  • CircuitBreakerState ····· 断路器状态
  • CircuitException ····· 断路器异常
  • ProtectedAction ····· 执行接口
  • TestBreaker ····· 测试

1.基本类信息

/**
 * 配置信息类
 *
 * @author bingo
 */
public class CircuitBreakerConfig {
    
    /* 关闭状态最大线程数 */
    public int MAX_THREAD_CLOSED = 100;

    /* 半开状态最大线程数 */
    public int MAX_THREAD_HALFOPEN = 5;

    /* 打开状态多少毫秒后,自动转为半关闭状态 */
    public long OPEN_TIME = 2000;

    /* 半开状态下,当连续成功了多少次以后,置为关闭 */
    public int SUCCESS_COUNT_CLOSED = 5;

    /* 关闭状态下,当连续失败了多少次以后,置为打开 */
    public int FAILURE_COUNT_OPEN = 5;
}
public interface ProtectedAction {
    /* 保护执行操作 */
    Object execute() throws Exception;

    boolean isBreakException(Exception e);
}

/**
 * 断路器
 *
 * @author bingo
 */
public class CircuitBreaker {

    // 断路器名称
    private String name;

    // 配置信息
    private CircuitBreakerConfig config;

    // 当前状态
    private CircuitBreakerState state = CircuitBreakerState.CLOSED;

    // 并发线程数
    private int threadCount = 0;

    // 成功次数,半开状态使用
    private int success_halfOpen = 0;

    // 失败次数,关闭状态使用
    private int failure_closed = 0;

    // 状态置为Open的时间点,打开状态使用
    private long openTime;

    // 构造器
    public CircuitBreaker(CircuitBreakerConfig config, String name) {
        super();
        this.config = config;
        this.name = name;
    }

    // 构造器
    public CircuitBreaker(String name) {
        this(new CircuitBreakerConfig(), name);
    }

    /* 保护并执行代码 */
    public Object execute(ProtectedAction action) throws CircuitException,
            Exception {
        doPrepare();
        try {
            Object result = action.execute();
            doSuccess();
            return result;
        } catch (Exception e) {
            if (action.isBreakException(e)) {
                doFailure();
            }
            throw e;
        } finally {
            doFinally();
        }
    }

    private synchronized void doPrepare() throws CircuitException {
        switch (this.state) {
            case OPEN:
                /*打开状态,未到期不执行,到期切换为半开状态*/
                if (threadCount >= config.MAX_THREAD_HALFOPEN
                        || System.currentTimeMillis() - openTime < config.OPEN_TIME) {
                    throw new CircuitException();
                }
                changeState(CircuitBreakerState.HALFOPEN);
                break;
            case CLOSED:
                /*关闭状态,超出线程不执行*/
                if (threadCount >= config.MAX_THREAD_CLOSED) {
                    throw new CircuitException();
                }
                break;
            case HALFOPEN:
                /*半开状态,超出线程不执行*/
                if (threadCount >= config.MAX_THREAD_HALFOPEN) {
                    throw new CircuitException();
                }
                break;
        }
        threadCount++;
    }

    private synchronized void doSuccess() {
        switch (this.state) {
            case OPEN:
                /*打开状态,重置失败次数*/
                failure_closed = 0;
                break;
            case HALFOPEN:
                /*半开状态,成功次数累计,达到阀值后置为关闭状态*/
                success_halfOpen++;
                if (success_halfOpen >= config.SUCCESS_COUNT_CLOSED) {
                    changeState(CircuitBreakerState.CLOSED);
                }
                break;
        }
    }

    // 执行失败
    private synchronized void doFailure() {
        switch (this.state) {
            case CLOSED:
                // 关闭状态,失败次数累计,达到阀值后置为打开状态
                failure_closed++;
                if (failure_closed >= config.FAILURE_COUNT_OPEN) {
                    changeState(CircuitBreakerState.OPEN);
                }
                break;
            case HALFOPEN:
                // 半开状态,置为打开状态
                changeState(CircuitBreakerState.OPEN);
                break;
        }
    }

    // 改变状态
    private void changeState(CircuitBreakerState state) {
        if (this.state == state) {
            return;
        }

        // 成功 失败次数置为 0
        this.success_halfOpen = 0;
        this.failure_closed = 0;

        if (state == CircuitBreakerState.OPEN) {
            // 打开的时间点
            this.openTime = System.currentTimeMillis();
            if (this.state == CircuitBreakerState.CLOSED) {
                System.out.println("CircuitBreaker[" + this.name + "] State Changed : " + this.state +
                        " --> " + state);
            } else {
                System.out.println("CircuitBreaker[" + this.name + "] State Changed : " + this.state +
                        " --> " + state);
            }
        } else {
            this.openTime = 0;
            if (state == CircuitBreakerState.CLOSED) {
                System.out.println("CircuitBreaker[" + this.name + "] State Changed : " + this.state +
                        " --> " + state);
            } else {
                System.out.println("CircuitBreaker[" + this.name + "] State Changed : " + this.state +
                        " --> " + state);
            }
        }
        this.state = state;
    }

    // 执行结束 并发线程数减 1
    private synchronized void doFinally() {
        this.threadCount--;
    }
}

2.实现原理

借用一张原理图
在这里插入图片描述
断路器分为三种状态:

  • 关闭状态:关闭状态可接收请求,同时失败后会计数器会+1,若连续失败次数超过最大值,则断路器状态切换为打开状态

  • 打开状态:判断打开时间是否到期,未到期拒绝所有请求,到期则切换为半开状态,可接受请求

  • 半开状态:可接收请求,若连续成功达到配置值,则切换为关闭状态,若失败,则切换为打开状态,且重置成功次数

执行流程:
初始为关闭状态,只限制最大线程数(也就是限流),执行成功重置失败次数。当连续失败达到最大值,则进入打开状态,此时拒绝所有请求,待断路器打开时间达到配置值,则进入半开状态,此时失败则返回打开状态,直到连续成功次数达到预定值,则断路器进入关闭状态。
执行结果

总结

至此,断路器的简单实现已经完成了,本次程序设计采用状态模式,根据执行情况来切换断路器状态,从而给系统缓冲时间恢复。当然实际情况可能更复杂,分布式、微服务现在很常见,与此类似使用的中间件也有很多如:Hystrix、Sentinel等。因个人能力有限,若有错误之处请及时指出,本人将及时纠正,感谢。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tiny丶bingo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值