package net.dubboclub.circuitbreaker; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.extension.Activate; import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.common.utils.ConfigUtils; import com.alibaba.dubbo.rpc.*; import net.dubboclub.circuitbreaker.exception.CircuitBreakerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** * Created by bieber on 2015/4/30. * Dubbo服务降级Filter * 通过拦截每个方法的请求,并且读取每个方法对服务降级条件的配置 * 从而自动进行服务降级,以及服务恢复 */ @Activate(group = {Constants.CONSUMER}) public class RemoteFacadeCircuitBreaker implements Filter { private static final Logger logger = LoggerFactory.getLogger("CIRCUITBREAKER"); private static final InetAddress localHost = Config.getLocalAddress(); //用于存储某个方法出现异常的计数器key:interfaceName.methodName,value:对应的异常计数器 private volatile ConcurrentHashMap<String,BreakCounter> breakCounterMap = new ConcurrentHashMap<String,BreakCounter>(); //对某个方法异常计数器处理器,用于分析记录的异常到当前实现是否失效了(为了满足在某个时间内出现异常次数),如果失效将从BreakCounter中移除 private BreakCounterLoop[] breakCounterLoops = new BreakCounterLoop[Runtime.getRuntime().availableProcessors()]; //获取dubbo的代理工程扩展 private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); //缓存服务降级代理类的Invoker,避免重复创建 private final static ConcurrentHashMap<String,Invoker> CIRCUIT_BREAKER_INVOKER_CACHE = new ConcurrentHashMap<String, Invoker>(); private volatile AtomicLong loopCount = new AtomicLong(0); public RemoteFacadeCircuitBreaker(){ String intervalConf = ConfigUtils.getProperty("dubbo.reference.check.break.marker.interval", "60000"); logger.info("[{}] has already been initialized circuit breaker,check break marker interval [{}]",localHost,intervalConf); long interval = Long.parseLong(intervalConf); for(int i=0;i<breakCounterLoops.length;i++){ BreakCounterLoop loop = new BreakCounterLoop(interval); breakCounterLoops[i]=loop; } } //获取下一个遍历器 private BreakCounterLoop nextLoop(){ return breakCounterLoops[((int) (loopCount.incrementAndGet() % breakCounterLoops.length))]; } public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { if(Config.checkFunctionSwitch(invoker, invocation)){ logger.info("[{}] had [{}] breaker",localHost,breakCounterMap.size()); return wrapBreakerInvoke(invoker,invocation); } Result result = invoker.invoke(invocation); toBeNormal(invoker,invocation); return result; } private Result wrapBreakerInvoke(Invoker<?> invoker, Invocation invocation) throws RpcException { //首先检查是否需要进入服务降级流程 if(checkNeedCircuitBreak(invoker, invocation)){ logger.info("[{}] activate the circuit break for url [{}],invoke method [{}]",localHost,invoker.getUrl(),invocation.getMethodName()); //进入服务降级 return doCircuitBreak(invoker, invocation); } try{ Result result = invoker.invoke(invocation); //将该服务从服务降级中恢复出来 toBeNormal(invoker, invocation); return result; }catch (RpcException e){ //如果是请求超时或者网络异常,进行异常统计 if(!e.isBiz()){ caughtException(invoker,invocation,e); } throw e; } } /** * 将服务恢复正常 * @param invoker * @param invocation */ private void toBeNormal(Invoker<?> invoker, Invocation invocation){ String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String method = invocation.getMethodName(); StringBuffer methodConfig = new StringBuffer(Config.DUBBO_REFERENCE_PREFIX); methodConfig.append(interfaceName).append(".").append(method); String methodKey = methodConfig.toString(); //从其中删除对应的异常计数器 BreakCounter counter = breakCounterMap.remove(methodKey); if(counter!=null){ logger.info("[{}] [{}.{}] to be normal",localHost,interfaceName,methodKey); //将这个counter设置为失效 counter.disable(); } } /** * 这里会判断当前调用服务的状态,分析判断是否需要进入降级状态 * 如果服务在指定的时间区间内累积的错误,达到了配置的次数,则进入服务降级 * 如果满足上面条件,并且满足重试机制,则也不会进入降级流程,而是触发远程服务调用 * @param invoker * @param invocation * @return */ private boolean checkNeedCircuitBreak(Invoker<?> invoker, Invocation invocation) { String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String method = invocation.getMethodName(); String methodKey = Config.getMethodPropertyName(invoker, invocation).toString(); int limit = Config.getBreakLimit(invoker, invocation); BreakCounter breakCounter = breakCounterMap.get(methodKey); if(breakCounter!=null&&breakCounter.isEnable()){ long currentExceptionCount = breakCounter.getCurrentExceptionCount(); long currentBreakCount = breakCounter.getCurrentBreakCount(); logger.info("[{}] check invoke [{}.{}] circuit break,current exception count [{}] limit [{}]", localHost, interfaceName, method, currentExceptionCount, limit); if(limit<=currentExceptionCount){ if(currentBreakCount>0&&needRetry(invoker, invocation, currentBreakCount)){ logger.info("[{}] retry invoke [{}.{}] current break count [{}]",localHost,interfaceName,method,currentBreakCount); breakCounter.incrementRetryTimes(); return false; } return true; } } return false; } private boolean needRetry(Invoker<?> invoker, Invocation invocation, long currentBreakCount){ String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String method = invocation.getMethodName(); int frequency = Config.getRetryFrequency(invoker, invocation); logger.info("[{}] check invoke [{}.{}] need retry,current break count [{}],retry frequency [{}]",localHost,interfaceName,method,currentBreakCount,frequency); if(currentBreakCount%frequency==0){ logger.info("[{}] retry invoke [{}.{}]",localHost,interfaceName,method); return true; } return false; } private <T extends Object> Result doCircuitBreak(Invoker<?> invoker, Invocation invocation) throws RpcException { String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String circuitBreaker = interfaceName+"CircuitBreak"; incrementBreakCount(invoker, invocation); try { logger.info("[{}] check has class [{}] to handle circuit break",localHost,circuitBreaker); Invoker<?> breakerInvoker =null; if(CIRCUIT_BREAKER_INVOKER_CACHE.containsKey(circuitBreaker)){ breakerInvoker = CIRCUIT_BREAKER_INVOKER_CACHE.get(circuitBreaker); }else{ Class<T> breakerType = (Class<T>) Class.forName(circuitBreaker); Class<T> interfaceType = (Class<T>) Class.forName(interfaceName); if(interfaceType.isAssignableFrom(breakerType)){ logger.info("[{}] handle circuit break by class [{}]",localHost,circuitBreaker); T breaker = breakerType.newInstance(); breakerInvoker = proxyFactory.getInvoker(breaker, interfaceType, invoker.getUrl()); Invoker<?> oldInvoker = CIRCUIT_BREAKER_INVOKER_CACHE.putIfAbsent(circuitBreaker, breakerInvoker); if(oldInvoker!=null){ breakerInvoker=oldInvoker; } } } if(breakerInvoker!=null){ return breakerInvoker.invoke(invocation); } } catch (Exception e) { logger.error("failed to invoke circuit breaker",e); } logger.info("[{}] handle circuit break by exception",localHost); CircuitBreakerException baseBusinessException = new CircuitBreakerException(interfaceName,invocation.getMethodName()); throw baseBusinessException; } private void incrementBreakCount(Invoker<?> invoker,Invocation invocation){ String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String method = invocation.getMethodName(); StringBuffer interfaceConfig = new StringBuffer(Config.DUBBO_REFERENCE_PREFIX); interfaceConfig.append(interfaceName); StringBuffer methodConfig = new StringBuffer(interfaceConfig.toString()); methodConfig.append(".").append(method); String methodKey = methodConfig.toString(); BreakCounter counter = breakCounterMap.get(methodKey); counter.incrementBreakCount(); } private void caughtException(Invoker<?> invoker,Invocation invocation,Exception e){ String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY); String method = invocation.getMethodName(); StringBuffer interfaceConfig = new StringBuffer(Config.DUBBO_REFERENCE_PREFIX); interfaceConfig.append(interfaceName); StringBuffer methodConfig = new StringBuffer(interfaceConfig.toString()); methodConfig.append(".").append(method); String methodKey = methodConfig.toString(); int timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); int limit = Config.getBreakLimit(invoker,invocation); //一个异常的有效期,是通过连续出现异常数量乘以每个调用的超时时间,比如你配置连续出现10次异常之后进行服务降级,并且每次服务调用的超时时间是2000ms的话,同时 //每个服务重试次数是为2次,那么就是在(2+1)*2000*10 ExceptionMarker breakMarker = new ExceptionMarker(System.currentTimeMillis(), limit*timeout,e); if(!breakCounterMap.containsKey(methodKey)){ BreakCounter oldValue = breakCounterMap.putIfAbsent(methodKey, new BreakCounter(methodKey)); //返回的oldValue为空,表示之前没有创建了赌赢的异常计数器,则需要对它分配一个loop if(oldValue==null){ nextLoop().register(breakCounterMap.get(methodKey)); } } BreakCounter counter=breakCounterMap.get(methodKey); counter.addExceptionMarker(breakMarker); logger.info("[{}] caught exception for rpc invoke [{}.{}],current exception count [{}]",localHost,interfaceName,method,counter.getCurrentExceptionCount()); } }