待整理。。 html
dubbo 啓動時默認有重試機制和超時機制。若是在必定的時間內,provider沒有返回,則認爲本次調用失敗。重試機制出如今調用失敗時,會再次調用,若是在配置的調用次數內都失敗,則認爲這次請求異常,消費端出現RpcException提示retry了多少次仍是失敗。算法
若是出現超時,一般是業務處理太慢,可在服務提供方執行 jstack PID > jstack.log 分析線程都卡在哪一個方法調用上。若是不能調優性能,請將timeout設大。api
dubbo消費端設置的超時時間須要根據業務實際狀況來設定,若是設置的太短,一些複雜業務須要很長時間完成,致使在設定的超時時間內沒法完成正常的業務處理。這樣消費端達到超時時間,那麼dubbo會進行重試,不合理的重試在一些特殊的業務場景下可能會引起不少問題。好比發送郵件,可能會發出多份重複郵件等。服務器
dubbo調用服務不成功時,默認會重試兩次。dubbo的路由機制,會把超時的請求路由到其餘機器上,而不是本機嘗試,因此dubbo的重試機制也能獲得必定程度的保證。可是不合理地配置重試次數,當失敗時會進行重試屢次,這樣在某個時間點出現性能問題,調用方繼續重試請求爲正常retries倍,容易引發服務雪崩。網絡
最佳實踐:架構
1.對於核心的服務中心,去除dubbo超時重試機制,並從新評估設置超時時間。
2.業務處理代碼必須放在服務端,客戶端只作參數驗證和服務調用,不涉及業務流程處理併發
在dubbo的provider和consumer的配置文件中,若是都配置了timeout的超時時間,dubbo默認以consumer中配置的時間爲準dom
provider.xml的配置:ide
<dubbo:service timeout="4000" retries="0" interface="com.dingding.tms.bms.service.BillingZfbCodOrderService" ref="billingZfbCodOrderService" registry="globalRegistry"/>
conusmer中的配置:性能
<dubbo:reference id="billingInterService" interface="com.dingding.tms.bms.service.BillingInterService" protocol="dubbo" check="false" registry="globalRegistry" timeout="3000"/>
最後這個service在調用時的超時時間就是3秒。
另外,超過3s後的表現以下:
(1) consumer會在超過3秒時獲得一個調用超時的異常。
(2) provider中代碼的執行不會由於超時而中斷,在執行完畢後,會獲得一個dubbo的警告。
(1) 在Provider上儘可能多地配置Consumer的屬性
緣由:
a. 做爲服務的提供者,比服務使用方更清楚服務性能參數,如調用的超時時間,合理的重試次數等
b. 在provider配置後,consumer不配置則自動使用provider的配置,即做爲了consumer端的缺省值。不然,consumer會使用consumer端的全局配置,這對於provider端每每是不可控的。
ps: 配置的優先級:
1. 方法級配置別優於接口級別,即小Scope優先
2. Consumer端配置優於Provider配置,優於全局配置
3. Dubbo Hard Code的配置值(默認)
根據規則2, 縱使消費端配置優於服務器配置,但消費端配置超時時間不能爲所欲爲,須要根據業務實際狀況來設定。若是設置的過短,複雜業務原本就須要很長時間完成,服務端沒法在設定的超時時間內完成業務處理; 若是設置太長,會因爲服務端或者網絡問題致使客戶端大量線程掛起。
配置示例
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" timeout="300" retry="2" loadbalance="random" actives="0"/> <dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService" timeout="300" retry="2" loadbalance="random" actives="0" > <dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" /> <dubbo:service/>
在provider端能夠配置的consumer屬性有:
1 timeout 方法調用超時時間
2 retry 失敗重試次數,缺省是2(加上第一次調用,共調用三次)
3 loadbalance 負責均衡算法(即多個provider如何挑選provider調用),缺省是隨機(random),還能夠有輪訓(roundrobin),最不活躍優先(leastactive, 指從consumer端併發調用效果最好的provider,這樣能夠相對減小併發的堆積)
4 actives 消費端最大併發調用限制,即當consumer對一個服務的併發調用達到上限後,新調用會wait直到超時。粒度上,在方法上配置(dubbo: method)則併發針對方法,在接口上配置(dubbo:service)則併發限制針對服務
(2) 在Provider上配置合理的provider屬性
<dubbo:protocol threads="200" /> <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" executes="200" > <dubbo:method name="findAllPerson" executes="50" /> </dubbo:service>
Provider上能夠配置的Provider端屬性有:
超時配置:
Dubbo消費端
全局超時配置
<dubbo:consumer timeout="5000" />
指定接口以及特定方法超時配置
<dubbo:reference interface="com.foo.BarService" timeout="2000"> <dubbo:method name="sayHello" timeout="3000" /> </dubbo:reference>
Dubbo服務端
全局超時配置
<dubbo:provider timeout="5000" />
指定接口以及特定方法超時配置
<dubbo:provider interface="com.foo.BarService" timeout="2000"> <dubbo:method name="sayHello" timeout="3000" /> </dubbo:provider>
Dubbo協議超時實現使用了Future模式,主要涉及類DubboInvoker,ResponseFuture, DefaultFuture。
ResponseFuture.get()在請求還未處理完或未到超時前一直是wait狀態;響應達到後,設置請求狀態,並進行notify喚醒。
public Object get() throws RemotingException { return get(timeout); } public Object get(int timeout) throws RemotingException { if (timeout <= 0) { timeout = Constants.DEFAULT_TIMEOUT; } if (! isDone()) { long start = System.currentTimeMillis(); lock.lock(); try { while (! isDone()) { done.await(timeout, TimeUnit.MILLISECONDS); if (isDone() || System.currentTimeMillis() - start > timeout) { break; } } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } if (! isDone()) { throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false)); } } return returnFromResponse(); }
public static void received(Channel channel, Response response) { try { DefaultFuture future = FUTURES.remove(response.getId()); if (future != null) { future.doReceived(response); } else { logger.warn("The timeout response finally returned at " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ", response " + response + (channel == null ? "" : ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress())); } } finally { CHANNELS.remove(response.getId()); } } private void doReceived(Response res) { lock.lock(); try { response = res; if (done != null) { done.signal(); } } finally { lock.unlock(); } if (callback != null) { invokeCallback(callback); } }
@see http://shiyanjun.cn/archives/325.html dubbo架構設計詳解