保险丝过载保护算法

保险丝过载保护代理类, 其核心思想是监控外部依赖服务的调用情况,如果外部服务调用超时或者失败超过一定 比率,则断开保险丝, 即不再调用外部服务而直接返回失败。保险丝断开状态会持续一段时间, 超时之后才重 新允许调用外部服务,此时若发现外部服务可用, 则合上保险丝,恢复到原始状态。保险丝有三个状态,正 常,生病和死亡状态,各个状态下的调用对应不同的处理逻辑。目前该算法在58大量使用,我们根据各个服务保 险丝的断开次数可以监控到客户端调用各个服务的服务质量。


保险丝算法流程如下:

保险丝过载保护

 

实现代码如下:

/**
 * 保险丝过载保护代理类, 其核心思想是监控外部依赖服务的调用情况,如果外部服务调用超时或者失败超过一定比率,则断开保险丝, 即不再调用外部服务而直接返回失败。
 * 保险丝断开状态会持续一段时间, 超时之后才重新允许调用外部服务, 此时若发现外部服务可用, 则合上保险丝,恢复到原始状态。
 * 保险丝有三个状态,正常,生病和死亡状态,各个状态下的调用对应不同的处理逻辑。
 *
 * @Author:caimin
 * @Since:2015年10月27日
 * @Version:
 */
public class FuseAbandonProxy implements InvocationHandler {

    private static final Log log = LogFactory.getLog(FuseAbandonProxy.class);
    //超时时间
    private int overtime = 0;
    //服务名称,主要用来记录日志
    private String name = null;
    //真实对象
    private Object proxy = null;

    //是否抛出拒绝异常
    private boolean isThrowRefuseException=false;

    public FuseAbandonProxy(Object proxy, String name) {
        this.proxy = proxy;
        this.overtime = 100;
        this.name = name;
        health.set(FULL_HEALTH);
    }

    public FuseAbandonProxy(Object proxy, String name,boolean isThrowRefuseException) {
        this.proxy = proxy;
        this.overtime = 100;
        this.name = name;
        this.isThrowRefuseException=isThrowRefuseException;
        health.set(FULL_HEALTH);
    }

    public FuseAbandonProxy(Object proxy, int overtime, String name) {
        this.proxy = proxy;
        this.overtime = overtime;
        this.name = name;
        health.set(FULL_HEALTH);
    }

    public FuseAbandonProxy(Object proxy, int overtime, String name,boolean isThrowRefuseException) {
        this.proxy = proxy;
        this.overtime = overtime;
        this.name = name;
        this.isThrowRefuseException=isThrowRefuseException;
        health.set(FULL_HEALTH);

    }

    /**
     * 当前总调用次数
     */
    private final AtomicInteger curTotalCount = new AtomicInteger(0);

    /**
     * 当前的超时次数
     */
    private final AtomicInteger curTimeoutCount = new AtomicInteger(0);


    /**
     * 当前的错误统计总数
     */
    private final AtomicInteger curExceptionCount = new AtomicInteger(0);

    /**
     * 当前拒绝次数
     */
    private final AtomicInteger curRefusedCount = new AtomicInteger(0);
    /**
     * 当前健康值
     */
    private final AtomicInteger health = new AtomicInteger(0);
    /**
     * 结束沉睡时间
     */
    private final AtomicLong endSleepTime = new AtomicLong();
    /**
     * 0代表正常
     * 1代表生病
     * 2代表死亡
     */
    private final AtomicInteger status = new AtomicInteger();

    /**
     * 满分健康值
     */
    private final static int FULL_HEALTH = 100;
    /**
     * 及格健康值
     */
    private final static int WEAK_HEALTH = 60;
    /**
     * 死亡健康值
     */
    private final static int DEAD_HEALTH = 0;
    /**
     * 超时损耗值
     */
    private final static int OVERTIME_LOSS = 5;

    /**
     * 异常损耗值
     */
    private final static int EXCEPTION_LOSS = 5;

    /**
     * 成功奖赏值
     */
    private final static int SUCCESS_AWARD = 1;

    /**
     * 生病沉睡时间
     */
    private final static int WEAK_SLEEP_TIME = 5 * 1000;

    /**
     * 死亡沉睡时间
     */
    private final static int DEAD_SLEEP_TIME = 60 * 1000;
    /**
     * 死亡状态
     */
    private final static int DEAD_STATUS = 2;
    /**
     * 生病状态
     */
    private final static int WAEK_STATUS = 1;
    /**
     * 正常状态
     */
    private final static int HEALTH_STATUS = 0;

    /**
     * 重新计数
     */
    private final static int RESET_CNT = 100000;





    private int addAndGetHealth(int val) {
        int h = health.addAndGet(val);
        if (h < DEAD_HEALTH) {
            health.set(DEAD_HEALTH);
            return DEAD_HEALTH;
        } else if (h > FULL_HEALTH) {
            health.set(FULL_HEALTH);
            return FULL_HEALTH;
        }
        return h;
    }

    private void reset() {
        log.info("reset name:" + name + ",curTotalCount:" + curTotalCount.get() +
                ",curTimeoutCount:" + curTimeoutCount.get() +
                ",curExceptionCount:" + curExceptionCount.get() +
                ",curRefusedCount:" + curRefusedCount + ",health:" + health);
        curTotalCount.set(0);
        curTimeoutCount.set(0);
        curRefusedCount.set(0);
        curExceptionCount.set(0);
        health.set(FULL_HEALTH);
        status.set(HEALTH_STATUS);
        endSleepTime.set(0);
    }

    public void printInfo() {
        log.info("info name:" + name + ",curTotalCount:" + curTotalCount.get() + ",curTimeoutCount:" + curTimeoutCount.get() + ",curRefusedCount:" + curRefusedCount + ",health:" + health + ",status:" + status + ":currTime" + System.currentTimeMillis() + ",endSleepTime:" + endSleepTime);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        long startTime = System.currentTimeMillis();
        //错误标记,标记是否发生异常,用户回血时判断使用
        boolean exceptionMark = false;
        curTotalCount.incrementAndGet();
        if(curTotalCount.get()>RESET_CNT)
        {
            reset();
        }
        //是否在沉睡
        long currEndSleepTime = endSleepTime.get();
        if (currEndSleepTime != 0) {
            //没有达到唤醒时间,直接拒绝服务,并且计数返回null
            if (startTime < currEndSleepTime) {
                log.warn("refuse name :" + name);
                curRefusedCount.addAndGet(1);
                if(isThrowRefuseException)
                {
                    throw new RefuseException("refuse name :" + name);
                }
                return null;
            } else {
                //清除沉睡时间
                endSleepTime.set(0);
                log.info("end sleep,name:" + name);
                //死亡的满血复活
                if (status.get() == DEAD_STATUS) {
                    status.set(HEALTH_STATUS);
                    health.set(FULL_HEALTH);
                    log.info("full alive,name:" + name);
                }
            }
        }
        try {
            Object result = method.invoke(this.proxy, args);
            return result;
        } catch (Exception e) {
            //如果报错,扣血,并且做错误标记
            log.warn("exception,name:" + name);
            curExceptionCount.incrementAndGet();
            addAndGetHealth(-EXCEPTION_LOSS);
            exceptionMark = true;
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            /**
             * 根据具体的超时时间和设置的超时时间做比较.得到超时倍率
             * 超时倍率越大.处罚越严重.
             * 扣分为 log2N*超时扣分(N=超时倍率) 扣分比率最大3倍
             **/
            long ot=endTime-startTime;
            long multiple=ot>overtime?ot/overtime:0;
            if (multiple>0) {
                    Double ratio=multiple>1?Math.log(multiple)/Math.log(2):multiple;
                    ratio=ratio>6?6:ratio;
                    log.warn("overtime,name:" + name + ",pay time" + (endTime - startTime));
                    curTimeoutCount.incrementAndGet();
                    addAndGetHealth(-OVERTIME_LOSS*ratio.intValue());
            } else {
                if (!exceptionMark) addAndGetHealth(SUCCESS_AWARD);
            }
            //如果生病严重,则沉睡
            int currHealth = health.get();
            if (currHealth <= DEAD_HEALTH && status.get() != DEAD_STATUS) {
                endSleepTime.set(endTime + DEAD_SLEEP_TIME);
                status.set(DEAD_STATUS);
                log.error("dead name:" + name + ",currHealth:" + currHealth +
                        ",curTimeoutCount:" + curTimeoutCount.get() +
                        ",curExceptionCount:"+curExceptionCount.get()+
                        ",refuseCount:"+curRefusedCount.get()+
                        ",curTotalCount" + curTotalCount.get());
            }
            //正常状态的需要设置为生病状态并沉睡
            else if (currHealth <= WEAK_HEALTH &&(status.get() == HEALTH_STATUS)) {
                endSleepTime.set(endTime + WEAK_SLEEP_TIME);
                status.set(WAEK_STATUS);
                log.error("weak name:" + name +
                        ",currHealth:" + currHealth +
                        ",curTimeoutCount:" + curTimeoutCount.get() +
                        ",curExceptionCount:"+curExceptionCount.get()+
                        ",refuseCount:"+curRefusedCount.get()+
                        ",curTotalCount:" + curTotalCount.get());

            }
            //如果生命回到健康,才设置为正常状态
            else if (currHealth >=FULL_HEALTH && status.get() != HEALTH_STATUS) {
                log.warn("live name:" + name +
                        ",currHealth:" + currHealth +
                        ",curTimeoutCount:" + curTimeoutCount.get() +
                        ",curExceptionCount:"+curExceptionCount.get()+
                        ",refuseCount:"+curRefusedCount.get()+
                        ",curTotalCount:" + curTotalCount.get());
                status.set(HEALTH_STATUS);
            }
        }
    }

}

转载于:https://my.oschina.net/iseeyou/blog/719150

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值