熔断分析->降级实现 解决雪崩

熔断的概念和作用:

'智能化的容错'   当调用满足失败次数,失败比例就会触发熔断器打开,有程序自动切断当前的RPC调用,来防止错误进一步扩大,实现一个熔断器主要考虑是三种模式:关闭  打开  半开 

作用: 防止我们的系统因为不断尝试,浪费资源,占用大量时间,暂时不允许这个服务器对外提供;

熔断三种模式: 关闭  打开   半开

关闭:

关闭: 默认情况下Circuit Breaker是关闭的,此时允许操作执行; Circuit Breaker内部记录着最近失败的次数,如果对应的操作执行失败,次数就会续一次; 如果在某个时间段内,失败次数(或者失败比率)达到阀值,Circuit Breaker就会切换到开启状态;开启状态中Circuit Breaker会启用一个超时计时器,设这个计时器的目的是给集群相应的时间来恢复故障,当计时器时间到的时候,Circuit Breaker会转换到半开启状态;

开启:

开启: 在此状态下,执行对应的操作将会立即失败并且抛出异常;

半开:


半开启: 在此状态下Circuit Breaker会允许执行一定数量的操作,如果所有操作全部成功,Circuit Breaker就会认为故障已经恢复,它就会转换到关闭状态,并且重置失败次数;如果其中,任意一次操作失败了Circuit Breakerjiu'hui认为故障仍然存在,所有它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)

MyAnswer博客


具体代码实现

<?php
/**
 * Created by PhpStorm.
 * User: myanswer
 * Date: 2019/1/20
 * Time: 11:39 PM
 */

class TestCircuitBreak
{
    public $redis;
    const RECORD = '{test}record'; //记录服务失败的次数 key
    const RONGDUANQI = '{test}circuit'; // 熔断器开启的key
    const STATUSOPEN = 1; //开启
    const STATUSClOS = 2;  //关闭
    const STATUSHalfopen = 3; //半开
    const Failuretimes = 5; //允许次数
    const HalfopenTime = 20; //多久进入半开状态,秒为单位

    /**
     * Redis连接对象
     * TestCircuitBreak constructor.
     * @throws RedisClusterException
     */
    public function __construct()
    {
        $this->redis = new RedisCluster(null,['45.40.207.143:6391','45.40.207.143:6392','45.40.207.143:6393']);
    }

    /**
     * 服务器在方法中进行过滤
     * @param $class
     * @param $method
     * @param $params
     * @param $fallback
     * @return string
     */
    public function invoke($class,$method,$params,$fallback){
        //获取当前执行的服务名+方法名
        $services = get_class($class).'_'.$method;
        //获取当前服务状态
        $status = $this->getServiceStatus($services);
        try{
                //服务开启状态,直接拒绝访问
                if ($status == self::STATUSOPEN){
                    return $fallback().'(状态开启)';
                }
                //是否满足半开状态,允许一些服务请求
                if ($status == self::STATUSHalfopen){
                    if (mt_rand(0,100)%2 == 0 ){
                        echo '半开状态允许请求';
                        $res =  $class->$method();
                        //添加成功次数
                        $this->redis->ZINCRBY(self::RECORD,1,$services);
                        return $res.'(半开起状态处理成功)';
                    }
                    return $fallback().'(半开状态请求拒绝)';
                }
              //熔断器关闭
              return  $class->$method();
        }catch (Exception $exception){
              //如果是关闭情况下出现错误
              if ($status == self::STATUSClOS){
                  //记录失败次数
                  $number =  $this->redis->ZINCRBY(self::RECORD,1,$services);
                  //达到失败次数,开启熔断器
                  if ($number >= self::Failuretimes){
                     //添加任务到延迟队列中,等段时间切换到半开起状态
                      $this->redis->zAdd(self::RONGDUANQI,time()+self::HalfopenTime,$services);
                  }
              }
             //如果是半开状态失败
              if ($status == self::STATUSHalfopen){
                 //将服务失败次数重置到最大,如果已经存在就更细
                 $this->redis->zAdd(self::RECORD,self::Failuretimes,$services);
                 //将任务添加到队列中
                 $this->redis->zAdd(self::RONGDUANQI,time()+self::HalfopenTime,$services);
              }
          return  $fallback();
        }

    }


    /**
     * 获取当前服务的状态
     * @param $services
     * @return int
     */
    public function getServiceStatus($services)
    {
        //获取当前服务状态次数
        $number = $this->redis->zScore(self::RECORD,$services);
        if ($number >= self::Failuretimes){
            return self::STATUSOPEN;  //熔断器开启状态
        }
        //服务半开起状态
        if ($number<0){
            return self::STATUSHalfopen;
        }
        return self::STATUSClOS; //默认返回关闭状态

    }

}
//商品服务类
class shop {

    public function test(){
       //throw new Exception('服务器出现异常');
      return  '服务请求成功';
    }

}
//每个服务都需要有一个 失败数量的回调函数
$fallback = function (){
   return '服务开小差了';

};
$TestCircuitBreak = new TestCircuitBreak();
echo  $TestCircuitBreak->invoke(new shop(),'test',[1],$fallback);

消费进程代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/1/21
 * Time: 20:37
 */

/**
 *  进程守护程序,定时消费 redis中的
 */

$redis = new RedisCluster(null,['45.40.207.143:6391','45.40.207.143:6392','45.40.207.143:6393']);
$count = -3; //半开起状态下,需要成功多少次关闭熔断器
$RECORD = '{test}record'; //记录服务失败的次数 key
$RONGDUANQI = '{test}circuit'; // 熔断器开启的key
while (true){
    echo '正在处理中!!!!'.PHP_EOL;
    $servers = $redis->zRangeByScore($RONGDUANQI,'-inf',time(),['withscores' => TRUE]);
    if (count($servers)>0){
        foreach ($servers as $key=>$v){
            //更新服务的,切换为半开起状态的条件
            $redis->zAdd($RECORD,$count,$key);
            //删除任务队列
            $redis->ZREM($RONGDUANQI,$key);
            echo '处理成功服务'.$key.PHP_EOL;
        }
    }
    sleep(1);
}


MyAnswer博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论