我和我同事Daniel排查的一個問題,原文是我同事Daniel寫的,我作了些修改了補充。git
咱們dubbox的provider端有不少service開發時沒有考慮到冪等問題,因而只能暫時關掉dubbo的重試功能。github
dubbo自帶的配置中有一個reties是能夠配置重試次數的,官方文檔上說默認兩次,設置成0能夠關閉重試。這種配置在使用xml 方式配置時是沒有問題的,然而用註解就比較坑。微信
上圖是同事寫的使用註解的測試類截圖,有沒有發現0是灰色的?看看編譯後的.class文件:ide
爲何呢,咱們看這個註解的源碼:oop
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface Reference { ... int retries() default 0; ... }
這個註解有個默認值0,編譯的時候被編譯器發現,因而優化掉了。而調用的時候:測試
從註解中反射找reties,若是沒找到就取默認值2,因而就形成了一個現象,只要不設置0就沒問題,0就會等同於2。上圖的完整方法:優化
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { List<Invoker<T>> copyinvokers = invokers; checkInvokers(copyinvokers, invocation); int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1; if (len <= 0) { len = 1; } // retry loop. RpcException le = null; // last exception. List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers. Set<String> providers = new HashSet<String>(len); for (int i = 0; i < len; i++) { //重試時,進行從新選擇,避免重試時invoker列表已發生變化. //注意:若是列表發生了變化,那麼invoked判斷會失效,由於invoker示例已經改變 if (i > 0) { checkWheatherDestoried(); copyinvokers = list(invocation); //從新檢查一下 checkInvokers(copyinvokers, invocation); } 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("Although retry the method " + invocation.getMethodName() + " in the service " + getInterface().getName() + " was successful by the provider " + invoker.getUrl().getAddress() + ", but there have been failed providers " + providers + " (" + providers.size() + "/" + copyinvokers.size() + ") from the registry " + directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + le.getMessage(), le); } return result; } catch (RpcException e) { if (e.isBiz()) { // biz exception. throw e; } le = e; } catch (Throwable e) { le = new RpcException(e.getMessage(), e); } finally { providers.add(invoker.getUrl().getAddress()); } } throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method " + invocation.getMethodName() + " in the service " + getInterface().getName() + ". Tried " + len + " times of the providers " + providers + " (" + providers.size() + "/" + copyinvokers.size() + ") from the registry " + directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le); }
這個方法就是遠程調用中會執行的Invoke鏈中的一個,經過:spa
int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1; if (len <= 0) { len = 1; }
上面這個判斷能夠猜到,文檔中說的值爲0關閉重試就是說的這句判斷了。上一句中對反射獲得的值進行了+1,這個+1就是加上了自己調用的一次,這裏就是最大可能執行的總次數。文檔中給出了一個方便人理解的值0來控制是否重試。因爲一些緣由只能用註解的方式,又沒法使用0,這裏的判斷是<=0,那咱們就在註解上設置-1或者更小均可以,因而結果就能夠知足須要了。通過測試發現確實能夠,因而問題解決的。注意,雖然問題解決了,但這個方式很是很差,只是限於一些緣由只能暫時採用這種彆扭的方式去作了,並不提倡。3d
==========================================================code
咱最近用的github:https://github.com/saaavsaaa
微信公衆號: