請點擊【關注】獲取更多網際網路和技術乾貨,頭條號IT徐胖子原創本文請勿轉載,感謝支持
假設服務提供者提供A服務,但是A服務並不穩定,如果服務消費者無法正常消費A服務就需要做降級處理,不再消費A服務而是返回一個mock值,這就是所謂服務降級。
但是在降級之前,消費者有可能重試消費服務A,或者直接返回空結果,或者延時重試,這就是所謂集群容錯策略。集群容錯策略有很多,本文我們分析故障轉移策略。
Failover策略被稱為故障轉移策略,這是集群容錯默認策略。當第一次消費服務失敗後,消費者會根據Failover策略選擇其它生產者再次消費,默認重試次數是2次。
<beans> <dubbo:application name=&34; /> <dubbo:registry address=&34; /> <dubbo:reference id=&34; cluster=&34; retries=&34; interface=&34; /></beans>
public class Consumer { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { &34; }); context.start(); HelloService helloService = (HelloService) context.getBean(&34;); String result = helloService.sayHello(&34;); System.out.println(&34; + result); }}
// 通過動態代理機制生成代理對象Proxy// Proxy持有InvokerInvocationHandler// InvokerInvocationHandler持有invoker = MockClusterInvoker(FailoverClusterInvoker)HelloService helloService = (HelloService) context.getBean(&34;);
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> { public FailoverClusterInvoker(Directory<T> directory) { super(directory); } @Override public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { // 所有生產者invokers List<Invoker<T>> copyInvokers = invokers; checkInvokers(copyInvokers, invocation); String methodName = RpcUtils.getMethodName(invocation); // 獲取重試次數 int len = getUrl().getMethodParameter(methodName, Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1; if (len <= 0) { len = 1; } RpcException le = null; // 已經調用過的生產者 List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); Set<String> providers = new HashSet<String>(len); // 重試直到達到最大次數 for (int i = 0; i < len; i++) { if (i > 0) { // 如果當前實例被銷毀則拋出異常 checkWhetherDestroyed(); // 根據路由策略選出可用生產者Invokers copyInvokers = list(invocation); // 重新檢查 checkInvokers(copyInvokers, invocation); } // 負載均衡選擇一個生產者invoker Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked); invoked.add(invoker); RpcContext.getContext().setInvokers((List) invoked); try { // 服務消費發起遠程調用 Result result = invoker.invoke(invocation); if (le != null && logger.isWarnEnabled()) { logger.warn(&34; + methodName + &34; + getInterface().getName() + &34; + invoker.getUrl().getAddress() + &34; + providers + &34; + providers.size() + &34; + copyInvokers.size() + &34; + directory.getUrl().getAddress() + &34; + NetUtils.getLocalHost() + &34; + Version.getVersion() + &34; + le.getMessage(), le); } return result; } catch (RpcException e) { // 業務異常直接拋出 if (e.isBiz()) { throw e; } le = e; } catch (Throwable e) { le = new RpcException(e.getMessage(), e); } finally { providers.add(invoker.getUrl().getAddress()); } } throw new RpcException(le.getCode(), &34; + methodName + &34; + getInterface().getName() + &34; + len + &34; + providers + &34; + providers.size() + &34; + copyInvokers.size() + &34; + directory.getUrl().getAddress() + &34; + NetUtils.getLocalHost() + &34; + Version.getVersion() + &34; + le.getMessage(), le.getCause() != null ? le.getCause() : le); }}
Failover策略是默認策略,所以我們在冪等設計時需要更加注意。例如服務生產者已經收到調用請求並已經處理成功,但是由於網絡原因返回結果時間被消費者認為超時,所以消費者會繼續重試調用,如果服務生產者沒有做好冪等就會出現重複處理問題,這個問題值得我們關注。
請點擊【關注】獲取更多網際網路和技術乾貨,頭條號IT徐胖子原創本文請勿轉載,感謝支持